1 /** \file
2 * Controls bars for some of Inkscape's tools
3 * (for some tools, they are in their own files)
4 */
6 /*
7 *
8 * Authors:
9 * MenTaLguY <mental@rydia.net>
10 * Lauris Kaplinski <lauris@kaplinski.com>
11 * bulia byak <buliabyak@users.sf.net>
12 * Frank Felfe <innerspace@iname.com>
13 * John Cliff <simarilius@yahoo.com>
14 * David Turner <novalis@gnu.org>
15 * Josh Andler <scislac@scislac.com>
16 * Jon A. Cruz <jon@joncruz.org>
17 *
18 * Copyright (C) 2004 David Turner
19 * Copyright (C) 2003 MenTaLguY
20 * Copyright (C) 1999-2006 authors
21 * Copyright (C) 2001-2002 Ximian, Inc.
22 *
23 * Released under GNU GPL, read the file 'COPYING' for more information
24 */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <cstring>
31 #include <string>
33 #include <gtkmm.h>
34 #include <gtk/gtk.h>
35 #include <iostream>
36 #include <sstream>
38 #include "widgets/button.h"
39 #include "widgets/widget-sizes.h"
40 #include "widgets/spw-utilities.h"
41 #include "widgets/spinbutton-events.h"
42 #include "dialogs/text-edit.h"
43 #include "dialogs/dialog-events.h"
45 #include "ui/widget/style-swatch.h"
47 #include "prefs-utils.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include <glibmm/i18n.h>
55 #include "helper/unit-menu.h"
56 #include "helper/units.h"
58 #include "inkscape.h"
59 #include "conn-avoid-ref.h"
62 #include "select-toolbar.h"
63 #include "gradient-toolbar.h"
65 #include "connector-context.h"
66 #include "node-context.h"
67 #include "shape-editor.h"
68 #include "tweak-context.h"
69 #include "sp-rect.h"
70 #include "box3d.h"
71 #include "box3d-context.h"
72 #include "sp-star.h"
73 #include "sp-spiral.h"
74 #include "sp-ellipse.h"
75 #include "sp-text.h"
76 #include "sp-flowtext.h"
77 #include "style.h"
78 #include "selection.h"
79 #include "selection-chemistry.h"
80 #include "document-private.h"
81 #include "desktop-style.h"
82 #include "../libnrtype/font-lister.h"
83 #include "../libnrtype/font-instance.h"
84 #include "../connection-pool.h"
85 #include "../prefs-utils.h"
86 #include "../inkscape-stock.h"
87 #include "icon.h"
88 #include "graphlayout/graphlayout.h"
90 #include "mod360.h"
92 #include "toolbox.h"
94 #include "flood-context.h"
96 #include "ink-action.h"
97 #include "ege-adjustment-action.h"
98 #include "ege-output-action.h"
99 #include "ege-select-one-action.h"
100 #include "helper/unit-tracker.h"
102 #include "svg/css-ostringstream.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
122 static void sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
128 static struct {
129 gchar const *type_name;
130 gchar const *data_name;
131 sp_verb_t verb;
132 sp_verb_t doubleclick_verb;
133 } const tools[] = {
134 { "SPSelectContext", "select_tool", SP_VERB_CONTEXT_SELECT, SP_VERB_CONTEXT_SELECT_PREFS},
135 { "SPNodeContext", "node_tool", SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
136 { "SPTweakContext", "tweak_tool", SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
137 { "SPZoomContext", "zoom_tool", SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
138 { "SPRectContext", "rect_tool", SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
139 { "Box3DContext", "3dbox_tool", SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
140 { "SPArcContext", "arc_tool", SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
141 { "SPStarContext", "star_tool", SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
142 { "SPSpiralContext", "spiral_tool", SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
143 { "SPPencilContext", "pencil_tool", SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
144 { "SPPenContext", "pen_tool", SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
145 { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
146 { "SPFloodContext", "paintbucket_tool", SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
147 { "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
148 { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
149 { "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
150 { "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
151 { NULL, NULL, 0, 0 }
152 };
154 static struct {
155 gchar const *type_name;
156 gchar const *data_name;
157 GtkWidget *(*create_func)(SPDesktop *desktop);
158 void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
159 gchar const *ui_name;
160 gint swatch_verb_id;
161 gchar const *swatch_tool;
162 gchar const *swatch_tip;
163 } const aux_toolboxes[] = {
164 { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep, "SelectToolbar",
165 SP_VERB_INVALID, 0, 0},
166 { "SPNodeContext", "node_toolbox", 0, sp_node_toolbox_prep, "NodeToolbar",
167 SP_VERB_INVALID, 0, 0},
168 { "SPTweakContext", "tweak_toolbox", 0, sp_tweak_toolbox_prep, "TweakToolbar",
169 SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", _("Color/opacity used for color tweaking")},
170 { "SPZoomContext", "zoom_toolbox", 0, sp_zoom_toolbox_prep, "ZoomToolbar",
171 SP_VERB_INVALID, 0, 0},
172 { "SPStarContext", "star_toolbox", 0, sp_star_toolbox_prep, "StarToolbar",
173 SP_VERB_CONTEXT_STAR_PREFS, "tools.shapes.star", _("Style of new stars")},
174 { "SPRectContext", "rect_toolbox", 0, sp_rect_toolbox_prep, "RectToolbar",
175 SP_VERB_CONTEXT_RECT_PREFS, "tools.shapes.rect", _("Style of new rectangles")},
176 { "Box3DContext", "3dbox_toolbox", 0, box3d_toolbox_prep, "3DBoxToolbar",
177 SP_VERB_CONTEXT_3DBOX_PREFS, "tools.shapes.3dbox", _("Style of new 3D boxes")},
178 { "SPArcContext", "arc_toolbox", 0, sp_arc_toolbox_prep, "ArcToolbar",
179 SP_VERB_CONTEXT_ARC_PREFS, "tools.shapes.arc", _("Style of new ellipses")},
180 { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep, "SpiralToolbar",
181 SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral", _("Style of new spirals")},
182 { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep, "PencilToolbar",
183 SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", _("Style of new paths created by Pencil")},
184 { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep, "PenToolbar",
185 SP_VERB_CONTEXT_PEN_PREFS, "tools.freehand.pen", _("Style of new paths created by Pen")},
186 { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
187 SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", _("Style of new calligraphic strokes")},
188 { "SPTextContext", "text_toolbox", sp_text_toolbox_new, 0, 0,
189 SP_VERB_INVALID, 0, 0},
190 { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep, "DropperToolbar",
191 SP_VERB_INVALID, 0, 0},
192 { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0, 0,
193 SP_VERB_INVALID, 0, 0},
194 { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
195 SP_VERB_INVALID, 0, 0},
196 { "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
197 SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", _("Style of Paint Bucket fill objects")},
198 { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
199 };
202 static gchar const * ui_descr =
203 "<ui>"
204 " <toolbar name='SelectToolbar'>"
205 " <toolitem action='EditSelectAll' />"
206 " <toolitem action='EditSelectAllInAllLayers' />"
207 " <toolitem action='EditDeselect' />"
208 " <separator />"
209 " <toolitem action='ObjectRotate90CCW' />"
210 " <toolitem action='ObjectRotate90' />"
211 " <toolitem action='ObjectFlipHorizontally' />"
212 " <toolitem action='ObjectFlipVertically' />"
213 " <separator />"
214 " <toolitem action='SelectionToBack' />"
215 " <toolitem action='SelectionLower' />"
216 " <toolitem action='SelectionRaise' />"
217 " <toolitem action='SelectionToFront' />"
218 " <separator />"
219 " <toolitem action='XAction' />"
220 " <toolitem action='YAction' />"
221 " <toolitem action='WidthAction' />"
222 " <toolitem action='LockAction' />"
223 " <toolitem action='HeightAction' />"
224 " <toolitem action='UnitsAction' />"
225 " <separator />"
226 " <toolitem action='transform_affect_label' />"
227 " <toolitem action='transform_stroke' />"
228 " <toolitem action='transform_corners' />"
229 " <toolitem action='transform_gradient' />"
230 " <toolitem action='transform_pattern' />"
231 " </toolbar>"
233 " <toolbar name='NodeToolbar'>"
234 " <toolitem action='NodeInsertAction' />"
235 " <toolitem action='NodeDeleteAction' />"
236 " <separator />"
237 " <toolitem action='NodeJoinAction' />"
238 " <toolitem action='NodeJoinSegmentAction' />"
239 " <toolitem action='NodeDeleteSegmentAction' />"
240 " <toolitem action='NodeBreakAction' />"
241 " <separator />"
242 " <toolitem action='NodeCuspAction' />"
243 " <toolitem action='NodeSmoothAction' />"
244 " <toolitem action='NodeSymmetricAction' />"
245 " <separator />"
246 " <toolitem action='NodeLineAction' />"
247 " <toolitem action='NodeCurveAction' />"
248 " <separator />"
249 " <toolitem action='ObjectToPath' />"
250 " <toolitem action='StrokeToPath' />"
251 " <separator />"
252 " <toolitem action='NodesShowHandlesAction' />"
253 " <toolitem action='NodesShowHelperpath' />"
254 " <separator />"
255 " <toolitem action='EditNextLPEParameterAction' />"
256 " <separator />"
257 " <toolitem action='NodeXAction' />"
258 " <toolitem action='NodeYAction' />"
259 " <toolitem action='NodeUnitsAction' />"
260 " </toolbar>"
262 " <toolbar name='TweakToolbar'>"
263 " <toolitem action='TweakWidthAction' />"
264 " <separator />"
265 " <toolitem action='TweakForceAction' />"
266 " <toolitem action='TweakPressureAction' />"
267 " <separator />"
268 " <toolitem action='TweakModeAction' />"
269 " <separator />"
270 " <toolitem action='TweakFidelityAction' />"
271 " <separator />"
272 " <toolitem action='TweakChannelsLabel' />"
273 " <toolitem action='TweakDoH' />"
274 " <toolitem action='TweakDoS' />"
275 " <toolitem action='TweakDoL' />"
276 " <toolitem action='TweakDoO' />"
277 " </toolbar>"
279 " <toolbar name='ZoomToolbar'>"
280 " <toolitem action='ZoomIn' />"
281 " <toolitem action='ZoomOut' />"
282 " <separator />"
283 " <toolitem action='Zoom1:0' />"
284 " <toolitem action='Zoom1:2' />"
285 " <toolitem action='Zoom2:1' />"
286 " <separator />"
287 " <toolitem action='ZoomSelection' />"
288 " <toolitem action='ZoomDrawing' />"
289 " <toolitem action='ZoomPage' />"
290 " <toolitem action='ZoomPageWidth' />"
291 " <separator />"
292 " <toolitem action='ZoomPrev' />"
293 " <toolitem action='ZoomNext' />"
294 " </toolbar>"
296 " <toolbar name='StarToolbar'>"
297 " <separator />"
298 " <toolitem action='StarStateAction' />"
299 " <separator />"
300 " <toolitem action='FlatAction' />"
301 " <separator />"
302 " <toolitem action='MagnitudeAction' />"
303 " <toolitem action='SpokeAction' />"
304 " <toolitem action='RoundednessAction' />"
305 " <toolitem action='RandomizationAction' />"
306 " <separator />"
307 " <toolitem action='StarResetAction' />"
308 " </toolbar>"
310 " <toolbar name='RectToolbar'>"
311 " <toolitem action='RectStateAction' />"
312 " <toolitem action='RectWidthAction' />"
313 " <toolitem action='RectHeightAction' />"
314 " <toolitem action='RadiusXAction' />"
315 " <toolitem action='RadiusYAction' />"
316 " <toolitem action='RectUnitsAction' />"
317 " <separator />"
318 " <toolitem action='RectResetAction' />"
319 " </toolbar>"
321 " <toolbar name='3DBoxToolbar'>"
322 " <toolitem action='3DBoxAngleXAction' />"
323 " <toolitem action='3DBoxVPXStateAction' />"
324 " <separator />"
325 " <toolitem action='3DBoxAngleYAction' />"
326 " <toolitem action='3DBoxVPYStateAction' />"
327 " <separator />"
328 " <toolitem action='3DBoxAngleZAction' />"
329 " <toolitem action='3DBoxVPZStateAction' />"
330 " </toolbar>"
332 " <toolbar name='SpiralToolbar'>"
333 " <toolitem action='SpiralStateAction' />"
334 " <toolitem action='SpiralRevolutionAction' />"
335 " <toolitem action='SpiralExpansionAction' />"
336 " <toolitem action='SpiralT0Action' />"
337 " <separator />"
338 " <toolitem action='SpiralResetAction' />"
339 " </toolbar>"
341 " <toolbar name='PenToolbar'>"
342 " </toolbar>"
344 " <toolbar name='PencilToolbar'>"
345 " </toolbar>"
347 " <toolbar name='CalligraphyToolbar'>"
348 " <separator />"
349 " <toolitem action='CalligraphyWidthAction' />"
350 " <toolitem action='PressureAction' />"
351 " <toolitem action='TraceAction' />"
352 " <toolitem action='ThinningAction' />"
353 " <separator />"
354 " <toolitem action='AngleAction' />"
355 " <toolitem action='TiltAction' />"
356 " <toolitem action='FixationAction' />"
357 " <separator />"
358 " <toolitem action='CapRoundingAction' />"
359 " <separator />"
360 " <toolitem action='TremorAction' />"
361 " <toolitem action='WiggleAction' />"
362 " <toolitem action='MassAction' />"
363 " <separator />"
364 " <toolitem action='CalligraphyResetAction' />"
365 " </toolbar>"
367 " <toolbar name='ArcToolbar'>"
368 " <toolitem action='ArcStateAction' />"
369 " <separator />"
370 " <toolitem action='ArcStartAction' />"
371 " <toolitem action='ArcEndAction' />"
372 " <separator />"
373 " <toolitem action='ArcOpenAction' />"
374 " <separator />"
375 " <toolitem action='ArcResetAction' />"
376 " <separator />"
377 " </toolbar>"
379 " <toolbar name='PaintbucketToolbar'>"
380 " <toolitem action='ChannelsAction' />"
381 " <separator />"
382 " <toolitem action='ThresholdAction' />"
383 " <separator />"
384 " <toolitem action='OffsetAction' />"
385 " <toolitem action='PaintbucketUnitsAction' />"
386 " <separator />"
387 " <toolitem action='AutoGapAction' />"
388 " <separator />"
389 " <toolitem action='PaintbucketResetAction' />"
390 " </toolbar>"
392 " <toolbar name='DropperToolbar'>"
393 " <toolitem action='DropperPickAlphaAction' />"
394 " <toolitem action='DropperSetAlphaAction' />"
395 " </toolbar>"
397 " <toolbar name='ConnectorToolbar'>"
398 " <toolitem action='ConnectorAvoidAction' />"
399 " <toolitem action='ConnectorIgnoreAction' />"
400 " <toolitem action='ConnectorSpacingAction' />"
401 " <toolitem action='ConnectorGraphAction' />"
402 " <toolitem action='ConnectorLengthAction' />"
403 " <toolitem action='ConnectorDirectedAction' />"
404 " <toolitem action='ConnectorOverlapAction' />"
405 " </toolbar>"
407 "</ui>"
408 ;
410 static GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop );
412 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
414 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
415 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
417 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
418 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
420 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
421 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
423 /* Global text entry widgets necessary for update */
424 /* GtkWidget *dropper_rgb_entry,
425 *dropper_opacity_entry ; */
426 // should be made a private member once this is converted to class
428 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
429 connection->disconnect();
430 delete connection;
431 }
433 static void purge_repr_listener( GObject* obj, GObject* tbl )
434 {
435 (void)obj;
436 Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
437 if (oldrepr) { // remove old listener
438 sp_repr_remove_listener_by_data(oldrepr, tbl);
439 Inkscape::GC::release(oldrepr);
440 oldrepr = 0;
441 g_object_set_data( tbl, "repr", NULL );
442 }
443 }
445 GtkWidget *
446 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
447 Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
448 Inkscape::UI::View::View *view, GtkTooltips *tt)
449 {
450 SPAction *action = verb->get_action(view);
451 if (!action) return NULL;
453 SPAction *doubleclick_action;
454 if (doubleclick_verb)
455 doubleclick_action = doubleclick_verb->get_action(view);
456 else
457 doubleclick_action = NULL;
459 /* fixme: Handle sensitive/unsensitive */
460 /* fixme: Implement sp_button_new_from_action */
461 GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
462 gtk_widget_show(b);
463 gtk_box_pack_start(GTK_BOX(t), b, FALSE, FALSE, 0);
465 return b;
466 }
468 GtkWidget *sp_toolbox_button_new_from_verb(GtkWidget *t, Inkscape::IconSize size, SPButtonType type, Inkscape::Verb *verb,
469 Inkscape::UI::View::View *view, GtkTooltips *tt)
470 {
471 return sp_toolbox_button_new_from_verb_with_doubleclick(t, size, type, verb, NULL, view, tt);
472 }
474 GtkWidget * sp_toolbox_button_normal_new_from_verb(GtkWidget *t, Inkscape::IconSize size, Inkscape::Verb *verb,
475 Inkscape::UI::View::View *view, GtkTooltips *tt)
476 {
477 return sp_toolbox_button_new_from_verb(t, size, SP_BUTTON_TYPE_NORMAL, verb, view, tt);
478 }
481 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
482 {
483 SPAction* targetAction = SP_ACTION(user_data);
484 if ( targetAction ) {
485 sp_action_perform( targetAction, NULL );
486 }
487 }
489 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
490 {
491 if ( data ) {
492 GtkAction* act = GTK_ACTION(data);
493 gtk_action_set_sensitive( act, sensitive );
494 }
495 }
497 static SPActionEventVector action_event_vector = {
498 {NULL},
499 NULL,
500 NULL,
501 sp_action_action_set_sensitive,
502 NULL,
503 NULL
504 };
506 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
507 {
508 GtkAction* act = 0;
510 SPAction* targetAction = verb->get_action(view);
511 InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size );
512 act = GTK_ACTION(inky);
513 gtk_action_set_sensitive( act, targetAction->sensitive );
515 g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
517 SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
518 nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
520 return act;
521 }
523 GtkActionGroup* create_or_fetch_actions( SPDesktop* desktop )
524 {
525 Inkscape::UI::View::View *view = desktop;
526 gint verbsToUse[] = {
527 // disabled until we have icons for them:
528 //find
529 //SP_VERB_EDIT_TILE,
530 //SP_VERB_EDIT_UNTILE,
531 SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
532 SP_VERB_DIALOG_DISPLAY,
533 SP_VERB_DIALOG_FILL_STROKE,
534 SP_VERB_DIALOG_NAMEDVIEW,
535 SP_VERB_DIALOG_TEXT,
536 SP_VERB_DIALOG_XML_EDITOR,
537 SP_VERB_EDIT_CLONE,
538 SP_VERB_EDIT_COPY,
539 SP_VERB_EDIT_CUT,
540 SP_VERB_EDIT_DUPLICATE,
541 SP_VERB_EDIT_PASTE,
542 SP_VERB_EDIT_REDO,
543 SP_VERB_EDIT_UNDO,
544 SP_VERB_EDIT_UNLINK_CLONE,
545 SP_VERB_FILE_EXPORT,
546 SP_VERB_FILE_IMPORT,
547 SP_VERB_FILE_NEW,
548 SP_VERB_FILE_OPEN,
549 SP_VERB_FILE_PRINT,
550 SP_VERB_FILE_SAVE,
551 SP_VERB_OBJECT_TO_CURVE,
552 SP_VERB_SELECTION_GROUP,
553 SP_VERB_SELECTION_OUTLINE,
554 SP_VERB_SELECTION_UNGROUP,
555 SP_VERB_ZOOM_1_1,
556 SP_VERB_ZOOM_1_2,
557 SP_VERB_ZOOM_2_1,
558 SP_VERB_ZOOM_DRAWING,
559 SP_VERB_ZOOM_IN,
560 SP_VERB_ZOOM_NEXT,
561 SP_VERB_ZOOM_OUT,
562 SP_VERB_ZOOM_PAGE,
563 SP_VERB_ZOOM_PAGE_WIDTH,
564 SP_VERB_ZOOM_PREV,
565 SP_VERB_ZOOM_SELECTION,
566 };
568 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
569 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
571 static std::map<SPDesktop*, GtkActionGroup*> groups;
572 GtkActionGroup* mainActions = 0;
573 if ( groups.find(desktop) != groups.end() ) {
574 mainActions = groups[desktop];
575 }
577 if ( !mainActions ) {
578 mainActions = gtk_action_group_new("main");
579 groups[desktop] = mainActions;
580 }
582 for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
583 Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
584 if ( verb ) {
585 if ( !gtk_action_group_get_action( mainActions, verb->get_id() ) ) {
586 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
587 gtk_action_group_add_action( mainActions, act );
588 }
589 }
590 }
592 return mainActions;
593 }
596 GtkWidget *
597 sp_tool_toolbox_new()
598 {
599 GtkTooltips *tt = gtk_tooltips_new();
600 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
602 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
603 g_object_set_data(G_OBJECT(tb), "tooltips", tt);
605 gtk_widget_set_sensitive(tb, FALSE);
607 GtkWidget *hb = gtk_handle_box_new();
608 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
609 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
610 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
612 gtk_container_add(GTK_CONTAINER(hb), tb);
613 gtk_widget_show(GTK_WIDGET(tb));
615 sigc::connection* conn = new sigc::connection;
616 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
618 return hb;
619 }
621 static void
622 aux_toolbox_attached(GtkHandleBox */*toolbox*/, GtkWidget *child)
623 {
624 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(FALSE));
625 gtk_widget_queue_resize(child);
626 }
628 static void
629 aux_toolbox_detached(GtkHandleBox */*toolbox*/, GtkWidget *child)
630 {
631 g_object_set_data(G_OBJECT(child), "is_detached", GINT_TO_POINTER(TRUE));
632 gtk_widget_queue_resize(child);
633 }
635 GtkWidget *
636 sp_aux_toolbox_new()
637 {
638 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
640 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
642 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
644 gtk_widget_set_sensitive(tb, FALSE);
646 GtkWidget *hb = gtk_handle_box_new();
647 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
648 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
649 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
651 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
652 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
654 gtk_container_add(GTK_CONTAINER(hb), tb);
655 gtk_widget_show(GTK_WIDGET(tb));
657 sigc::connection* conn = new sigc::connection;
658 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
660 return hb;
661 }
663 //####################################
664 //# Commands Bar
665 //####################################
667 GtkWidget *
668 sp_commands_toolbox_new()
669 {
670 GtkWidget *tb = gtk_vbox_new(FALSE, 0);
672 gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
674 g_object_set_data(G_OBJECT(tb), "desktop", NULL);
675 gtk_widget_set_sensitive(tb, FALSE);
677 GtkWidget *hb = gtk_handle_box_new();
678 gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
679 gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
680 gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
682 g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(aux_toolbox_attached), (gpointer)tb);
683 g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(aux_toolbox_detached), (gpointer)tb);
685 gtk_container_add(GTK_CONTAINER(hb), tb);
686 gtk_widget_show(GTK_WIDGET(tb));
688 sigc::connection* conn = new sigc::connection;
689 g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
691 return hb;
692 }
694 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
695 gchar const *label, gchar const *shortLabel, gchar const *tooltip,
696 gchar const *path, gchar const *data, gdouble def,
697 GtkWidget *focusTarget,
698 GtkWidget *us,
699 GObject *dataKludge,
700 gboolean altx, gchar const *altx_mark,
701 gdouble lower, gdouble upper, gdouble step, gdouble page,
702 gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
703 void (*callback)(GtkAdjustment *, GObject *),
704 gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
705 {
706 GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
707 lower, upper, step, page, page ) );
708 if (us) {
709 sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
710 }
712 gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
714 EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
715 if ( shortLabel ) {
716 g_object_set( act, "short_label", shortLabel, NULL );
717 }
719 if ( (descrCount > 0) && descrLabels && descrValues ) {
720 ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
721 }
723 if ( focusTarget ) {
724 ege_adjustment_action_set_focuswidget( act, focusTarget );
725 }
727 if ( altx && altx_mark ) {
728 g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
729 }
731 if ( dataKludge ) {
732 g_object_set_data( dataKludge, data, adj );
733 }
735 // Using a cast just to make sure we pass in the right kind of function pointer
736 g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
738 return act;
739 }
742 //####################################
743 //# node editing callbacks
744 //####################################
746 /**
747 * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
748 */
749 static ShapeEditor *get_current_shape_editor()
750 {
751 if (!SP_ACTIVE_DESKTOP) {
752 return NULL;
753 }
755 SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
757 if (!SP_IS_NODE_CONTEXT(event_context)) {
758 return NULL;
759 }
761 return SP_NODE_CONTEXT(event_context)->shape_editor;
762 }
765 void
766 sp_node_path_edit_add(void)
767 {
768 ShapeEditor *shape_editor = get_current_shape_editor();
769 if (shape_editor) shape_editor->add_node();
770 }
772 void
773 sp_node_path_edit_delete(void)
774 {
775 ShapeEditor *shape_editor = get_current_shape_editor();
776 if (shape_editor) shape_editor->delete_nodes();
777 }
779 void
780 sp_node_path_edit_delete_segment(void)
781 {
782 ShapeEditor *shape_editor = get_current_shape_editor();
783 if (shape_editor) shape_editor->delete_segment();
784 }
786 void
787 sp_node_path_edit_break(void)
788 {
789 ShapeEditor *shape_editor = get_current_shape_editor();
790 if (shape_editor) shape_editor->break_at_nodes();
791 }
793 void
794 sp_node_path_edit_join(void)
795 {
796 ShapeEditor *shape_editor = get_current_shape_editor();
797 if (shape_editor) shape_editor->join_nodes();
798 }
800 void
801 sp_node_path_edit_join_segment(void)
802 {
803 ShapeEditor *shape_editor = get_current_shape_editor();
804 if (shape_editor) shape_editor->join_segments();
805 }
807 void
808 sp_node_path_edit_toline(void)
809 {
810 ShapeEditor *shape_editor = get_current_shape_editor();
811 if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
812 }
814 void
815 sp_node_path_edit_tocurve(void)
816 {
817 ShapeEditor *shape_editor = get_current_shape_editor();
818 if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
819 }
821 void
822 sp_node_path_edit_cusp(void)
823 {
824 ShapeEditor *shape_editor = get_current_shape_editor();
825 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
826 }
828 void
829 sp_node_path_edit_smooth(void)
830 {
831 ShapeEditor *shape_editor = get_current_shape_editor();
832 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
833 }
835 void
836 sp_node_path_edit_symmetrical(void)
837 {
838 ShapeEditor *shape_editor = get_current_shape_editor();
839 if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
840 }
842 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
843 bool show = gtk_toggle_action_get_active( act );
844 prefs_set_int_attribute ("tools.nodes", "show_handles", show ? 1 : 0);
845 ShapeEditor *shape_editor = get_current_shape_editor();
846 if (shape_editor) shape_editor->show_handles(show);
847 }
849 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
850 bool show = gtk_toggle_action_get_active( act );
851 prefs_set_int_attribute ("tools.nodes", "show_helperpath", show ? 1 : 0);
852 ShapeEditor *shape_editor = get_current_shape_editor();
853 if (shape_editor) shape_editor->show_helperpath(show);
854 }
856 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
857 sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
858 }
860 /* is called when the node selection is modified */
861 static void
862 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
863 {
864 GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
865 GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
866 GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
867 GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
869 // quit if run by the attr_changed listener
870 if (g_object_get_data( tbl, "freeze" )) {
871 return;
872 }
874 // in turn, prevent listener from responding
875 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
877 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
878 SPUnit const *unit = tracker->getActiveUnit();
880 ShapeEditor *shape_editor = get_current_shape_editor();
881 if (shape_editor && shape_editor->has_nodepath()) {
882 Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
883 int n_selected = 0;
884 if (nodepath) {
885 n_selected = nodepath->numSelected();
886 }
888 if (n_selected == 0) {
889 gtk_action_set_sensitive(xact, FALSE);
890 gtk_action_set_sensitive(yact, FALSE);
891 } else {
892 gtk_action_set_sensitive(xact, TRUE);
893 gtk_action_set_sensitive(yact, TRUE);
894 NR::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
895 NR::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
897 if (n_selected == 1) {
898 NR::Point sel_node = nodepath->singleSelectedCoords();
899 if (oldx != sel_node[NR::X] || oldy != sel_node[NR::Y]) {
900 gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[NR::X], *unit));
901 gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[NR::Y], *unit));
902 }
903 } else {
904 NR::Maybe<NR::Coord> x = sp_node_selected_common_coord(nodepath, NR::X);
905 NR::Maybe<NR::Coord> y = sp_node_selected_common_coord(nodepath, NR::Y);
906 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
907 /* Note: Currently x and y will always have a value, even if the coordinates of the
908 selected nodes don't coincide (in this case we use the coordinates of the center
909 of the bounding box). So the entries are never set to zero. */
910 // FIXME: Maybe we should clear the entry if several nodes are selected
911 // instead of providing a kind of average value
912 gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
913 gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
914 }
915 }
916 }
917 } else {
918 // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
919 gtk_action_set_sensitive(xact, FALSE);
920 gtk_action_set_sensitive(yact, FALSE);
921 }
923 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
924 }
926 static void
927 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
928 {
929 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
931 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
932 SPUnit const *unit = tracker->getActiveUnit();
934 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
935 prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
936 }
938 // quit if run by the attr_changed listener
939 if (g_object_get_data( tbl, "freeze" )) {
940 return;
941 }
943 // in turn, prevent listener from responding
944 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
946 ShapeEditor *shape_editor = get_current_shape_editor();
947 if (shape_editor && shape_editor->has_nodepath()) {
948 double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
949 if (!strcmp(value_name, "x")) {
950 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::X);
951 }
952 if (!strcmp(value_name, "y")) {
953 sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, NR::Y);
954 }
955 }
957 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
958 }
960 static void
961 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
962 {
963 sp_node_path_value_changed(adj, tbl, "x");
964 }
966 static void
967 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
968 {
969 sp_node_path_value_changed(adj, tbl, "y");
970 }
972 //################################
973 //## Node Editing Toolbox ##
974 //################################
976 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
977 {
978 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
979 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
980 g_object_set_data( holder, "tracker", tracker );
982 {
983 InkAction* inky = ink_action_new( "NodeInsertAction",
984 _("Insert node"),
985 _("Insert new nodes into selected segments"),
986 "node_insert",
987 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
988 g_object_set( inky, "short_label", _("Insert"), NULL );
989 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
990 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
991 }
993 {
994 InkAction* inky = ink_action_new( "NodeDeleteAction",
995 _("Delete node"),
996 _("Delete selected nodes"),
997 "node_delete",
998 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
999 g_object_set( inky, "short_label", _("Delete"), NULL );
1000 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1001 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1002 }
1004 {
1005 InkAction* inky = ink_action_new( "NodeJoinAction",
1006 _("Join endnodes"),
1007 _("Join selected endnodes"),
1008 "node_join",
1009 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1010 g_object_set( inky, "short_label", _("Join"), NULL );
1011 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1012 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1013 }
1015 {
1016 InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1017 _("Join Segment"),
1018 _("Join selected endnodes with a new segment"),
1019 "node_join_segment",
1020 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1021 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1022 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1023 }
1025 {
1026 InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1027 _("Delete Segment"),
1028 _("Split path between two non-endpoint nodes"),
1029 "node_delete_segment",
1030 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1031 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1032 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1033 }
1035 {
1036 InkAction* inky = ink_action_new( "NodeBreakAction",
1037 _("Node Break"),
1038 _("Break path at selected nodes"),
1039 "node_break",
1040 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1041 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1042 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1043 }
1045 {
1046 InkAction* inky = ink_action_new( "NodeCuspAction",
1047 _("Node Cusp"),
1048 _("Make selected nodes corner"),
1049 "node_cusp",
1050 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1051 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1052 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1053 }
1055 {
1056 InkAction* inky = ink_action_new( "NodeSmoothAction",
1057 _("Node Smooth"),
1058 _("Make selected nodes smooth"),
1059 "node_smooth",
1060 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1061 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1062 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1063 }
1065 {
1066 InkAction* inky = ink_action_new( "NodeSymmetricAction",
1067 _("Node Symmetric"),
1068 _("Make selected nodes symmetric"),
1069 "node_symmetric",
1070 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1071 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1072 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1073 }
1075 {
1076 InkAction* inky = ink_action_new( "NodeLineAction",
1077 _("Node Line"),
1078 _("Make selected segments lines"),
1079 "node_line",
1080 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1081 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1082 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1083 }
1085 {
1086 InkAction* inky = ink_action_new( "NodeCurveAction",
1087 _("Node Curve"),
1088 _("Make selected segments curves"),
1089 "node_curve",
1090 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
1091 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1092 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1093 }
1095 {
1096 InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1097 _("Show Handles"),
1098 _("Show the Bezier handles of selected nodes"),
1099 "nodes_show_handles",
1100 Inkscape::ICON_SIZE_DECORATION );
1101 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1102 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1103 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1104 }
1106 {
1107 InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1108 _("Show Outline"),
1109 _("Show the outline of the path"),
1110 "nodes_show_helperpath",
1111 Inkscape::ICON_SIZE_DECORATION );
1112 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1113 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1114 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1115 }
1117 {
1118 InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1119 _("Next Path Effect Parameter"),
1120 _("Show next Path Effect parameter for editing"),
1121 "edit_next_parameter",
1122 Inkscape::ICON_SIZE_DECORATION );
1123 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1124 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1125 }
1127 /* X coord of selected node(s) */
1128 {
1129 EgeAdjustmentAction* eact = 0;
1130 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1131 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1132 eact = create_adjustment_action( "NodeXAction",
1133 _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1134 "tools.nodes", "Xcoord", 0,
1135 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1136 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1137 labels, values, G_N_ELEMENTS(labels),
1138 sp_node_path_x_value_changed );
1139 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1140 g_object_set_data( holder, "nodes_x_action", eact );
1141 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1142 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1143 }
1145 /* Y coord of selected node(s) */
1146 {
1147 EgeAdjustmentAction* eact = 0;
1148 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1149 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1150 eact = create_adjustment_action( "NodeYAction",
1151 _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1152 "tools.nodes", "Ycoord", 0,
1153 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1154 -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1155 labels, values, G_N_ELEMENTS(labels),
1156 sp_node_path_y_value_changed );
1157 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1158 g_object_set_data( holder, "nodes_y_action", eact );
1159 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1160 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1161 }
1163 // add the units menu
1164 {
1165 GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1166 gtk_action_group_add_action( mainActions, act );
1167 }
1169 sigc::connection *connection = new sigc::connection (
1170 desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(sp_node_toolbox_coord_changed), (GObject *)holder))
1171 );
1173 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
1174 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1175 } // end of sp_node_toolbox_prep()
1178 //########################
1179 //## Zoom Toolbox ##
1180 //########################
1182 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1183 {
1184 // no custom GtkAction setup needed
1185 } // end of sp_zoom_toolbox_prep()
1187 void
1188 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1189 {
1190 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_tool_toolbox, update_tool_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1191 }
1194 void
1195 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1196 {
1197 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_aux_toolbox, update_aux_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1198 }
1200 void
1201 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1202 {
1203 toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)), desktop, setup_commands_toolbox, update_commands_toolbox, static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox), "event_context_connection")));
1204 }
1206 static void
1207 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1208 {
1209 gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1210 SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1212 if (old_desktop) {
1213 GList *children, *iter;
1215 children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1216 for ( iter = children ; iter ; iter = iter->next ) {
1217 gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1218 }
1219 g_list_free(children);
1220 }
1222 g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1224 if (desktop) {
1225 gtk_widget_set_sensitive(toolbox, TRUE);
1226 setup_func(toolbox, desktop);
1227 update_func(desktop, desktop->event_context, toolbox);
1228 *conn = desktop->connectEventContextChanged
1229 (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1230 } else {
1231 gtk_widget_set_sensitive(toolbox, FALSE);
1232 }
1234 } // end of toolbox_set_desktop()
1237 static void
1238 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1239 {
1240 GtkTooltips *tooltips=GTK_TOOLTIPS(g_object_get_data(G_OBJECT(toolbox), "tooltips"));
1241 gint shrinkLeft = prefs_get_int_attribute_limited( "toolbox.tools", "small", 0, 0, 1 );
1242 if ( (shrinkLeft == 0) && (prefs_get_int_attribute_limited( "toolbox.tools", "small", 1, 0, 1 ) == 1) ) {
1243 // "toolbox.tools" was not set. Fallback to older value
1244 shrinkLeft = prefs_get_int_attribute_limited( "toolbox.left", "small", 0, 0, 1 );
1246 // Copy the setting forwards
1247 prefs_set_int_attribute( "toolbox.tools", "small", shrinkLeft );
1248 }
1249 Inkscape::IconSize toolboxSize = shrinkLeft ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1251 for (int i = 0 ; tools[i].type_name ; i++ ) {
1252 GtkWidget *button =
1253 sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
1254 SP_BUTTON_TYPE_TOGGLE,
1255 Inkscape::Verb::get(tools[i].verb),
1256 Inkscape::Verb::get(tools[i].doubleclick_verb),
1257 desktop,
1258 tooltips );
1260 g_object_set_data( G_OBJECT(toolbox), tools[i].data_name,
1261 (gpointer)button );
1262 }
1263 }
1266 static void
1267 update_tool_toolbox( SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox )
1268 {
1269 gchar const *const tname = ( eventcontext
1270 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1271 : NULL );
1272 for (int i = 0 ; tools[i].type_name ; i++ ) {
1273 SPButton *button = SP_BUTTON(g_object_get_data(G_OBJECT(toolbox), tools[i].data_name));
1274 sp_button_toggle_set_down(button, tname && !strcmp(tname, tools[i].type_name));
1275 }
1276 }
1278 static void
1279 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1280 {
1281 GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1282 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1283 GtkUIManager* mgr = gtk_ui_manager_new();
1284 GError* errVal = 0;
1285 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1286 gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1288 std::map<std::string, GtkWidget*> dataHolders;
1290 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1291 if ( aux_toolboxes[i].prep_func ) {
1292 // converted to GtkActions and UIManager
1294 GtkWidget* kludge = gtk_hbox_new( FALSE, 0 );
1295 g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1296 g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1297 dataHolders[aux_toolboxes[i].type_name] = kludge;
1298 aux_toolboxes[i].prep_func( desktop, mainActions, G_OBJECT(kludge) );
1299 } else {
1301 GtkWidget *sub_toolbox = 0;
1302 if (aux_toolboxes[i].create_func == NULL)
1303 sub_toolbox = sp_empty_toolbox_new(desktop);
1304 else {
1305 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1306 }
1308 gtk_size_group_add_widget( grouper, sub_toolbox );
1310 gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1311 g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1313 }
1314 }
1316 // Second pass to create toolbars *after* all GtkActions are created
1317 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1318 if ( aux_toolboxes[i].prep_func ) {
1319 // converted to GtkActions and UIManager
1321 GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1323 GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1324 gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1326 gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1327 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1328 g_free( tmp );
1329 tmp = 0;
1331 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1332 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1333 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1334 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1335 }
1336 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1339 gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1341 if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1342 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, aux_toolboxes[i].swatch_tip );
1343 swatch->setDesktop( desktop );
1344 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1345 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1346 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1347 gtk_table_attach( GTK_TABLE(holder), swatch_, 1, 2, 0, 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), AUX_BETWEEN_BUTTON_GROUPS, 0 );
1348 }
1350 gtk_widget_show_all( holder );
1351 sp_set_font_size_smaller( holder );
1353 gtk_size_group_add_widget( grouper, holder );
1355 gtk_container_add( GTK_CONTAINER(toolbox), holder );
1356 g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1357 }
1358 }
1360 g_object_unref( G_OBJECT(grouper) );
1361 }
1363 static void
1364 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1365 {
1366 gchar const *tname = ( eventcontext
1367 ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1368 : NULL );
1369 for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1370 GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1371 if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1372 gtk_widget_show_all(sub_toolbox);
1373 g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1374 } else {
1375 gtk_widget_hide(sub_toolbox);
1376 }
1377 }
1378 }
1380 static void
1381 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1382 {
1383 gchar const * descr =
1384 "<ui>"
1385 " <toolbar name='CommandsToolbar'>"
1386 " <toolitem action='FileNew' />"
1387 " <toolitem action='FileOpen' />"
1388 " <toolitem action='FileSave' />"
1389 " <toolitem action='FilePrint' />"
1390 " <separator />"
1391 " <toolitem action='FileImport' />"
1392 " <toolitem action='FileExport' />"
1393 " <separator />"
1394 " <toolitem action='EditUndo' />"
1395 " <toolitem action='EditRedo' />"
1396 " <separator />"
1397 " <toolitem action='EditCopy' />"
1398 " <toolitem action='EditCut' />"
1399 " <toolitem action='EditPaste' />"
1400 " <separator />"
1401 " <toolitem action='ZoomSelection' />"
1402 " <toolitem action='ZoomDrawing' />"
1403 " <toolitem action='ZoomPage' />"
1404 " <separator />"
1405 " <toolitem action='EditDuplicate' />"
1406 " <toolitem action='EditClone' />"
1407 " <toolitem action='EditUnlinkClone' />"
1408 " <separator />"
1409 " <toolitem action='SelectionGroup' />"
1410 " <toolitem action='SelectionUnGroup' />"
1411 " <separator />"
1412 " <toolitem action='DialogFillStroke' />"
1413 " <toolitem action='DialogText' />"
1414 " <toolitem action='DialogXMLEditor' />"
1415 " <toolitem action='DialogAlignDistribute' />"
1416 " <separator />"
1417 " <toolitem action='DialogPreferences' />"
1418 " <toolitem action='DialogDocumentProperties' />"
1419 " </toolbar>"
1420 "</ui>";
1421 GtkActionGroup* mainActions = create_or_fetch_actions( desktop );
1424 GtkUIManager* mgr = gtk_ui_manager_new();
1425 GError* errVal = 0;
1427 gtk_ui_manager_insert_action_group( mgr, mainActions, 0 );
1428 gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1430 GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1431 if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1432 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1433 }
1434 gint shrinkTop = prefs_get_int_attribute_limited( "toolbox", "small", 1, 0, 1 );
1435 Inkscape::IconSize toolboxSize = shrinkTop ? Inkscape::ICON_SIZE_SMALL_TOOLBAR : Inkscape::ICON_SIZE_LARGE_TOOLBAR;
1436 gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1439 gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1440 }
1442 static void
1443 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1444 {
1445 }
1447 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1448 {
1449 gtk_widget_show(toolbox_toplevel);
1450 GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1452 GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1453 if (!shown_toolbox) {
1454 return;
1455 }
1456 gtk_widget_show(toolbox);
1458 gtk_widget_show_all(shown_toolbox);
1459 }
1461 void
1462 aux_toolbox_space(GtkWidget *tb, gint space)
1463 {
1464 gtk_box_pack_start(GTK_BOX(tb), gtk_hbox_new(FALSE, 0), FALSE, FALSE, space);
1465 }
1467 static GtkWidget *
1468 sp_empty_toolbox_new(SPDesktop *desktop)
1469 {
1470 GtkWidget *tbl = gtk_hbox_new(FALSE, 0);
1471 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1472 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1474 gtk_widget_show_all(tbl);
1475 sp_set_font_size_smaller (tbl);
1477 return tbl;
1478 }
1480 // helper UI functions
1482 GtkWidget *
1483 sp_tb_spinbutton(
1484 gchar *label, gchar const *tooltip,
1485 gchar const *path, gchar const *data, gdouble def,
1486 GtkWidget *us,
1487 GtkWidget *tbl,
1488 gboolean altx, gchar const *altx_mark,
1489 gdouble lower, gdouble upper, gdouble step, gdouble page,
1490 void (*callback)(GtkAdjustment *, GtkWidget *),
1491 gdouble climb = 0.1, guint digits = 3, double factor = 1.0)
1492 {
1493 GtkTooltips *tt = gtk_tooltips_new();
1495 GtkWidget *hb = gtk_hbox_new(FALSE, 1);
1497 GtkWidget *l = gtk_label_new(label);
1498 gtk_widget_show(l);
1499 gtk_misc_set_alignment(GTK_MISC(l), 1.0, 0.5);
1500 gtk_container_add(GTK_CONTAINER(hb), l);
1502 GtkObject *a = gtk_adjustment_new(prefs_get_double_attribute(path, data, def) * factor,
1503 lower, upper, step, page, page);
1504 gtk_object_set_data(GTK_OBJECT(tbl), data, a);
1505 if (us)
1506 sp_unit_selector_add_adjustment(SP_UNIT_SELECTOR(us), GTK_ADJUSTMENT(a));
1508 GtkWidget *sb = gtk_spin_button_new(GTK_ADJUSTMENT(a), climb, digits);
1509 gtk_tooltips_set_tip(tt, sb, tooltip, NULL);
1510 if (altx)
1511 gtk_object_set_data(GTK_OBJECT(sb), altx_mark, sb);
1512 gtk_widget_set_size_request(sb,
1513 (upper <= 1.0 || digits == 0)? AUX_SPINBUTTON_WIDTH_SMALL - 10: AUX_SPINBUTTON_WIDTH_SMALL,
1514 AUX_SPINBUTTON_HEIGHT);
1515 gtk_widget_show(sb);
1516 gtk_signal_connect(GTK_OBJECT(sb), "focus-in-event", GTK_SIGNAL_FUNC(spinbutton_focus_in), tbl);
1517 gtk_signal_connect(GTK_OBJECT(sb), "key-press-event", GTK_SIGNAL_FUNC(spinbutton_keypress), tbl);
1518 gtk_container_add(GTK_CONTAINER(hb), sb);
1519 gtk_signal_connect(GTK_OBJECT(a), "value_changed", GTK_SIGNAL_FUNC(callback), tbl);
1521 return hb;
1522 }
1524 #define MODE_LABEL_WIDTH 70
1526 //########################
1527 //## Star ##
1528 //########################
1530 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1531 {
1532 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1534 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1535 // do not remember prefs if this call is initiated by an undo change, because undoing object
1536 // creation sets bogus values to its attributes before it is deleted
1537 prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1538 }
1540 // quit if run by the attr_changed listener
1541 if (g_object_get_data( dataKludge, "freeze" )) {
1542 return;
1543 }
1545 // in turn, prevent listener from responding
1546 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1548 bool modmade = false;
1550 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1551 GSList const *items = selection->itemList();
1552 for (; items != NULL; items = items->next) {
1553 if (SP_IS_STAR((SPItem *) items->data)) {
1554 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1555 sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1556 sp_repr_set_svg_double(repr, "sodipodi:arg2",
1557 (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1558 + M_PI / (gint)adj->value));
1559 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1560 modmade = true;
1561 }
1562 }
1563 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1564 _("Star: Change number of corners"));
1566 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1567 }
1569 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1570 {
1571 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1573 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1574 prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1575 }
1577 // quit if run by the attr_changed listener
1578 if (g_object_get_data( dataKludge, "freeze" )) {
1579 return;
1580 }
1582 // in turn, prevent listener from responding
1583 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1585 bool modmade = false;
1586 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1587 GSList const *items = selection->itemList();
1588 for (; items != NULL; items = items->next) {
1589 if (SP_IS_STAR((SPItem *) items->data)) {
1590 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1592 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1593 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1594 if (r2 < r1) {
1595 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1596 } else {
1597 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1598 }
1600 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1601 modmade = true;
1602 }
1603 }
1605 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1606 _("Star: Change spoke ratio"));
1608 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1609 }
1611 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1612 {
1613 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1614 bool flat = ege_select_one_action_get_active( act ) == 0;
1616 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1617 prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1618 flat ? "true" : "false" );
1619 }
1621 // quit if run by the attr_changed listener
1622 if (g_object_get_data( dataKludge, "freeze" )) {
1623 return;
1624 }
1626 // in turn, prevent listener from responding
1627 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1629 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1630 GSList const *items = selection->itemList();
1631 GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1632 bool modmade = false;
1634 if ( prop_action ) {
1635 gtk_action_set_sensitive( prop_action, !flat );
1636 }
1638 for (; items != NULL; items = items->next) {
1639 if (SP_IS_STAR((SPItem *) items->data)) {
1640 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1641 repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1642 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1643 modmade = true;
1644 }
1645 }
1647 if (modmade) {
1648 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1649 flat ? _("Make polygon") : _("Make star"));
1650 }
1652 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1653 }
1655 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1656 {
1657 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1659 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1660 prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1661 }
1663 // quit if run by the attr_changed listener
1664 if (g_object_get_data( dataKludge, "freeze" )) {
1665 return;
1666 }
1668 // in turn, prevent listener from responding
1669 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1671 bool modmade = false;
1673 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1674 GSList const *items = selection->itemList();
1675 for (; items != NULL; items = items->next) {
1676 if (SP_IS_STAR((SPItem *) items->data)) {
1677 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1678 sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
1679 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1680 modmade = true;
1681 }
1682 }
1683 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1684 _("Star: Change rounding"));
1686 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1687 }
1689 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1690 {
1691 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1693 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1694 prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
1695 }
1697 // quit if run by the attr_changed listener
1698 if (g_object_get_data( dataKludge, "freeze" )) {
1699 return;
1700 }
1702 // in turn, prevent listener from responding
1703 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1705 bool modmade = false;
1707 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1708 GSList const *items = selection->itemList();
1709 for (; items != NULL; items = items->next) {
1710 if (SP_IS_STAR((SPItem *) items->data)) {
1711 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1712 sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
1713 SP_OBJECT(items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
1714 modmade = true;
1715 }
1716 }
1717 if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1718 _("Star: Change randomization"));
1720 g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1721 }
1724 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
1725 gchar const */*old_value*/, gchar const */*new_value*/,
1726 bool /*is_interactive*/, gpointer data)
1727 {
1728 GtkWidget *tbl = GTK_WIDGET(data);
1730 // quit if run by the _changed callbacks
1731 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
1732 return;
1733 }
1735 // in turn, prevent callbacks from responding
1736 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
1738 GtkAdjustment *adj = 0;
1740 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1741 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1743 if (!strcmp(name, "inkscape:randomized")) {
1744 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
1745 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
1746 } else if (!strcmp(name, "inkscape:rounded")) {
1747 adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
1748 gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
1749 } else if (!strcmp(name, "inkscape:flatsided")) {
1750 GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
1751 char const *flatsides = repr->attribute("inkscape:flatsided");
1752 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
1753 if ( flatsides && !strcmp(flatsides,"false") ) {
1754 ege_select_one_action_set_active( flat_action, 1 );
1755 gtk_action_set_sensitive( prop_action, TRUE );
1756 } else {
1757 ege_select_one_action_set_active( flat_action, 0 );
1758 gtk_action_set_sensitive( prop_action, FALSE );
1759 }
1760 } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
1761 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
1762 gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1763 gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1764 if (r2 < r1) {
1765 gtk_adjustment_set_value(adj, r2/r1);
1766 } else {
1767 gtk_adjustment_set_value(adj, r1/r2);
1768 }
1769 } else if (!strcmp(name, "sodipodi:sides")) {
1770 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
1771 gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
1772 }
1774 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
1775 }
1778 static Inkscape::XML::NodeEventVector star_tb_repr_events =
1779 {
1780 NULL, /* child_added */
1781 NULL, /* child_removed */
1782 star_tb_event_attr_changed,
1783 NULL, /* content_changed */
1784 NULL /* order_changed */
1785 };
1788 /**
1789 * \param selection Should not be NULL.
1790 */
1791 static void
1792 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
1793 {
1794 int n_selected = 0;
1795 Inkscape::XML::Node *repr = NULL;
1797 purge_repr_listener( tbl, tbl );
1799 for (GSList const *items = selection->itemList();
1800 items != NULL;
1801 items = items->next)
1802 {
1803 if (SP_IS_STAR((SPItem *) items->data)) {
1804 n_selected++;
1805 repr = SP_OBJECT_REPR((SPItem *) items->data);
1806 }
1807 }
1809 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
1811 if (n_selected == 0) {
1812 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
1813 } else if (n_selected == 1) {
1814 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
1816 if (repr) {
1817 g_object_set_data( tbl, "repr", repr );
1818 Inkscape::GC::anchor(repr);
1819 sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
1820 sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
1821 }
1822 } else {
1823 // FIXME: implement averaging of all parameters for multiple selected stars
1824 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
1825 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
1826 }
1827 }
1830 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
1831 {
1832 // FIXME: in this and all other _default functions, set some flag telling the value_changed
1833 // callbacks to lump all the changes for all selected objects in one undo step
1835 GtkAdjustment *adj = 0;
1837 // fixme: make settable in prefs!
1838 gint mag = 5;
1839 gdouble prop = 0.5;
1840 gboolean flat = FALSE;
1841 gdouble randomized = 0;
1842 gdouble rounded = 0;
1844 EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
1845 ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
1847 GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1848 gtk_action_set_sensitive( sb2, !flat );
1850 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
1851 gtk_adjustment_set_value(adj, mag);
1852 gtk_adjustment_value_changed(adj);
1854 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
1855 gtk_adjustment_set_value(adj, prop);
1856 gtk_adjustment_value_changed(adj);
1858 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
1859 gtk_adjustment_set_value(adj, rounded);
1860 gtk_adjustment_value_changed(adj);
1862 adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
1863 gtk_adjustment_set_value(adj, randomized);
1864 gtk_adjustment_value_changed(adj);
1865 }
1868 void
1869 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
1870 {
1871 GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
1872 if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
1873 GtkWidget *l = gtk_label_new(NULL);
1874 gtk_label_set_markup(GTK_LABEL(l), title);
1875 gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
1876 gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
1877 gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
1878 }
1881 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1882 {
1883 {
1884 EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
1885 ege_output_action_set_use_markup( act, TRUE );
1886 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1887 g_object_set_data( holder, "mode_action", act );
1888 }
1890 {
1891 EgeAdjustmentAction* eact = 0;
1892 gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
1893 bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
1895 /* Flatsided checkbox */
1896 {
1897 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
1899 GtkTreeIter iter;
1900 gtk_list_store_append( model, &iter );
1901 gtk_list_store_set( model, &iter,
1902 0, _("Polygon"),
1903 1, _("Regular polygon (with one handle) instead of a star"),
1904 2, "star_flat",
1905 -1 );
1907 gtk_list_store_append( model, &iter );
1908 gtk_list_store_set( model, &iter,
1909 0, _("Star"),
1910 1, _("Star instead of a regular polygon (with one handle)"),
1911 2, "star_angled",
1912 -1 );
1914 EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
1915 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
1916 g_object_set_data( holder, "flat_action", act );
1918 ege_select_one_action_set_appearance( act, "full" );
1919 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
1920 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
1921 ege_select_one_action_set_icon_column( act, 2 );
1922 ege_select_one_action_set_tooltip_column( act, 1 );
1924 ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
1925 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
1926 }
1928 /* Magnitude */
1929 {
1930 gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
1931 gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
1932 eact = create_adjustment_action( "MagnitudeAction",
1933 _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
1934 "tools.shapes.star", "magnitude", 3,
1935 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1936 3, 1024, 1, 5,
1937 labels, values, G_N_ELEMENTS(labels),
1938 sp_stb_magnitude_value_changed,
1939 1.0, 0 );
1940 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1941 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1942 }
1944 /* Spoke ratio */
1945 {
1946 gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
1947 gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
1948 eact = create_adjustment_action( "SpokeAction",
1949 _("Spoke ratio"), _("Spoke ratio:"),
1950 // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
1951 // Base radius is the same for the closest handle.
1952 _("Base radius to tip radius ratio"),
1953 "tools.shapes.star", "proportion", 0.5,
1954 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1955 0.01, 1.0, 0.01, 0.1,
1956 labels, values, G_N_ELEMENTS(labels),
1957 sp_stb_proportion_value_changed );
1958 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1959 g_object_set_data( holder, "prop_action", eact );
1960 }
1962 if ( !isFlatSided ) {
1963 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1964 } else {
1965 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1966 }
1968 /* Roundedness */
1969 {
1970 gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
1971 gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
1972 eact = create_adjustment_action( "RoundednessAction",
1973 _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
1974 "tools.shapes.star", "rounded", 0.0,
1975 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1976 -10.0, 10.0, 0.01, 0.1,
1977 labels, values, G_N_ELEMENTS(labels),
1978 sp_stb_rounded_value_changed );
1979 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1980 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1981 }
1983 /* Randomization */
1984 {
1985 gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
1986 gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
1987 eact = create_adjustment_action( "RandomizationAction",
1988 _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
1989 "tools.shapes.star", "randomized", 0.0,
1990 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
1991 -10.0, 10.0, 0.001, 0.01,
1992 labels, values, G_N_ELEMENTS(labels),
1993 sp_stb_randomized_value_changed, 0.1, 3 );
1994 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1995 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
1996 }
1997 }
1999 {
2000 /* Reset */
2001 {
2002 GtkAction* act = gtk_action_new( "StarResetAction",
2003 _("Defaults"),
2004 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2005 GTK_STOCK_CLEAR );
2006 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2007 gtk_action_group_add_action( mainActions, act );
2008 gtk_action_set_sensitive( act, TRUE );
2009 }
2010 }
2012 sigc::connection *connection = new sigc::connection(
2013 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2014 );
2015 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2016 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2017 }
2020 //########################
2021 //## Rect ##
2022 //########################
2024 static void sp_rtb_sensitivize( GObject *tbl )
2025 {
2026 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2027 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2028 GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2030 if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2031 gtk_action_set_sensitive( not_rounded, FALSE );
2032 } else {
2033 gtk_action_set_sensitive( not_rounded, TRUE );
2034 }
2035 }
2038 static void
2039 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2040 void (*setter)(SPRect *, gdouble))
2041 {
2042 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2044 UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2045 SPUnit const *unit = tracker->getActiveUnit();
2047 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2048 prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2049 }
2051 // quit if run by the attr_changed listener
2052 if (g_object_get_data( tbl, "freeze" )) {
2053 return;
2054 }
2056 // in turn, prevent listener from responding
2057 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2059 bool modmade = false;
2060 Inkscape::Selection *selection = sp_desktop_selection(desktop);
2061 for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2062 if (SP_IS_RECT(items->data)) {
2063 if (adj->value != 0) {
2064 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2065 } else {
2066 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2067 }
2068 modmade = true;
2069 }
2070 }
2072 sp_rtb_sensitivize( tbl );
2074 if (modmade) {
2075 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2076 _("Change rectangle"));
2077 }
2079 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2080 }
2082 static void
2083 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2084 {
2085 sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2086 }
2088 static void
2089 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2090 {
2091 sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2092 }
2094 static void
2095 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2096 {
2097 sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2098 }
2100 static void
2101 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2102 {
2103 sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2104 }
2108 static void
2109 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2110 {
2111 GtkAdjustment *adj = 0;
2113 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2114 gtk_adjustment_set_value(adj, 0.0);
2115 // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2116 gtk_adjustment_value_changed(adj);
2118 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2119 gtk_adjustment_set_value(adj, 0.0);
2120 gtk_adjustment_value_changed(adj);
2122 sp_rtb_sensitivize( obj );
2123 }
2125 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2126 gchar const */*old_value*/, gchar const */*new_value*/,
2127 bool /*is_interactive*/, gpointer data)
2128 {
2129 GObject *tbl = G_OBJECT(data);
2131 // quit if run by the _changed callbacks
2132 if (g_object_get_data( tbl, "freeze" )) {
2133 return;
2134 }
2136 // in turn, prevent callbacks from responding
2137 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2139 UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2140 SPUnit const *unit = tracker->getActiveUnit();
2142 gpointer item = g_object_get_data( tbl, "item" );
2143 if (item && SP_IS_RECT(item)) {
2144 {
2145 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2146 gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2147 gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2148 }
2150 {
2151 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2152 gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2153 gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2154 }
2156 {
2157 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2158 gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2159 gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2160 }
2162 {
2163 GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2164 gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2165 gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2166 }
2167 }
2169 sp_rtb_sensitivize( tbl );
2171 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2172 }
2175 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2176 NULL, /* child_added */
2177 NULL, /* child_removed */
2178 rect_tb_event_attr_changed,
2179 NULL, /* content_changed */
2180 NULL /* order_changed */
2181 };
2183 /**
2184 * \param selection should not be NULL.
2185 */
2186 static void
2187 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2188 {
2189 int n_selected = 0;
2190 Inkscape::XML::Node *repr = NULL;
2191 SPItem *item = NULL;
2193 if ( g_object_get_data( tbl, "repr" ) ) {
2194 g_object_set_data( tbl, "item", NULL );
2195 }
2196 purge_repr_listener( tbl, tbl );
2198 for (GSList const *items = selection->itemList();
2199 items != NULL;
2200 items = items->next) {
2201 if (SP_IS_RECT((SPItem *) items->data)) {
2202 n_selected++;
2203 item = (SPItem *) items->data;
2204 repr = SP_OBJECT_REPR(item);
2205 }
2206 }
2208 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2210 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2212 if (n_selected == 0) {
2213 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2215 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2216 gtk_action_set_sensitive(w, FALSE);
2217 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2218 gtk_action_set_sensitive(h, FALSE);
2220 } else if (n_selected == 1) {
2221 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2222 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2224 GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2225 gtk_action_set_sensitive(w, TRUE);
2226 GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2227 gtk_action_set_sensitive(h, TRUE);
2229 if (repr) {
2230 g_object_set_data( tbl, "repr", repr );
2231 g_object_set_data( tbl, "item", item );
2232 Inkscape::GC::anchor(repr);
2233 sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2234 sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2235 }
2236 } else {
2237 // FIXME: implement averaging of all parameters for multiple selected
2238 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2239 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2240 sp_rtb_sensitivize( tbl );
2241 }
2242 }
2245 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2246 {
2247 EgeAdjustmentAction* eact = 0;
2249 {
2250 EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2251 ege_output_action_set_use_markup( act, TRUE );
2252 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2253 g_object_set_data( holder, "mode_action", act );
2254 }
2256 // rx/ry units menu: create
2257 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2258 //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2259 // fixme: add % meaning per cent of the width/height
2260 tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2261 g_object_set_data( holder, "tracker", tracker );
2263 /* W */
2264 {
2265 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2266 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2267 eact = create_adjustment_action( "RectWidthAction",
2268 _("Width"), _("W:"), _("Width of rectangle"),
2269 "tools.shapes.rect", "width", 0,
2270 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2271 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2272 labels, values, G_N_ELEMENTS(labels),
2273 sp_rtb_width_value_changed );
2274 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2275 g_object_set_data( holder, "width_action", eact );
2276 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2277 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2278 }
2280 /* H */
2281 {
2282 gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2283 gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2284 eact = create_adjustment_action( "RectHeightAction",
2285 _("Height"), _("H:"), _("Height of rectangle"),
2286 "tools.shapes.rect", "height", 0,
2287 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2288 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2289 labels, values, G_N_ELEMENTS(labels),
2290 sp_rtb_height_value_changed );
2291 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2292 g_object_set_data( holder, "height_action", eact );
2293 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2294 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2295 }
2297 /* rx */
2298 {
2299 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2300 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2301 eact = create_adjustment_action( "RadiusXAction",
2302 _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2303 "tools.shapes.rect", "rx", 0,
2304 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2305 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2306 labels, values, G_N_ELEMENTS(labels),
2307 sp_rtb_rx_value_changed);
2308 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2309 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2310 }
2312 /* ry */
2313 {
2314 gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2315 gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2316 eact = create_adjustment_action( "RadiusYAction",
2317 _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2318 "tools.shapes.rect", "ry", 0,
2319 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2320 0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2321 labels, values, G_N_ELEMENTS(labels),
2322 sp_rtb_ry_value_changed);
2323 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2324 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2325 }
2327 // add the units menu
2328 {
2329 GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2330 gtk_action_group_add_action( mainActions, act );
2331 }
2333 /* Reset */
2334 {
2335 InkAction* inky = ink_action_new( "RectResetAction",
2336 _("Not rounded"),
2337 _("Make corners sharp"),
2338 "squared_corner",
2339 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2340 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2341 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2342 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2343 g_object_set_data( holder, "not_rounded", inky );
2344 }
2346 g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2347 sp_rtb_sensitivize( holder );
2349 sigc::connection *connection = new sigc::connection(
2350 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2351 );
2352 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2353 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2354 }
2356 //########################
2357 //## 3D Box ##
2358 //########################
2360 // normalize angle so that it lies in the interval [0,360]
2361 static double box3d_normalize_angle (double a) {
2362 double angle = a + ((int) (a/360.0))*360;
2363 if (angle < 0) {
2364 angle += 360.0;
2365 }
2366 return angle;
2367 }
2369 static void
2370 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2371 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2372 // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2373 // have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2374 // are reset).
2375 bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2377 if (is_infinite) {
2378 gtk_toggle_action_set_active(tact, TRUE);
2379 gtk_action_set_sensitive(act, TRUE);
2381 double angle = persp3d_get_infinite_angle(persp, axis);
2382 if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2383 gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2384 }
2385 } else {
2386 gtk_toggle_action_set_active(tact, FALSE);
2387 gtk_action_set_sensitive(act, FALSE);
2388 }
2389 }
2391 static void
2392 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2393 if (!persp_repr) {
2394 g_print ("No perspective given to box3d_resync_toolbar().\n");
2395 return;
2396 }
2398 GtkWidget *tbl = GTK_WIDGET(data);
2399 GtkAdjustment *adj = 0;
2400 GtkAction *act = 0;
2401 GtkToggleAction *tact = 0;
2402 Persp3D *persp = persp3d_get_from_repr(persp_repr);
2403 {
2404 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2405 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2406 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2408 box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2409 }
2410 {
2411 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2412 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2413 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2415 box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2416 }
2417 {
2418 adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2419 act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2420 tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2422 box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2423 }
2424 }
2426 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2427 gchar const */*old_value*/, gchar const */*new_value*/,
2428 bool /*is_interactive*/, gpointer data)
2429 {
2430 GtkWidget *tbl = GTK_WIDGET(data);
2432 // quit if run by the attr_changed listener
2433 // note: it used to work without the differently called freeze_ attributes (here and in
2434 // box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2435 if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2436 return;
2437 }
2439 // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2440 // sp_document_maybe_done() when the document is undo insensitive)
2441 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2443 // TODO: Only update the appropriate part of the toolbar
2444 // if (!strcmp(name, "inkscape:vp_z")) {
2445 box3d_resync_toolbar(repr, G_OBJECT(tbl));
2446 // }
2448 Persp3D *persp = persp3d_get_from_repr(repr);
2449 persp3d_update_box_reprs(persp);
2451 g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2452 }
2454 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2455 {
2456 NULL, /* child_added */
2457 NULL, /* child_removed */
2458 box3d_persp_tb_event_attr_changed,
2459 NULL, /* content_changed */
2460 NULL /* order_changed */
2461 };
2463 /**
2464 * \param selection Should not be NULL.
2465 */
2466 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2467 // Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2468 static void
2469 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2470 {
2471 // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2472 // disable the angle entry fields for this direction (otherwise entering a value in them should only
2473 // update the perspectives with infinite VPs and leave the other ones untouched).
2475 Inkscape::XML::Node *persp_repr = NULL;
2476 purge_repr_listener(tbl, tbl);
2478 SPItem *item = selection->singleItem();
2479 if (item && SP_IS_BOX3D(item)) {
2480 // FIXME: Also deal with multiple selected boxes
2481 SPBox3D *box = SP_BOX3D(item);
2482 Persp3D *persp = box3d_get_perspective(box);
2483 persp_repr = SP_OBJECT_REPR(persp);
2484 if (persp_repr) {
2485 g_object_set_data(tbl, "repr", persp_repr);
2486 Inkscape::GC::anchor(persp_repr);
2487 sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2488 sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2489 }
2491 inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2492 prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2494 box3d_resync_toolbar(persp_repr, tbl);
2495 }
2496 }
2498 static void
2499 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2500 {
2501 SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2502 SPDocument *document = sp_desktop_document(desktop);
2504 // quit if run by the attr_changed listener
2505 // note: it used to work without the differently called freeze_ attributes (here and in
2506 // box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2507 if (g_object_get_data( dataKludge, "freeze_attr" )) {
2508 return;
2509 }
2511 // in turn, prevent listener from responding
2512 g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2514 //Persp3D *persp = document->current_persp3d;
2515 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2516 if (sel_persps.empty()) {
2517 // this can happen when the document is created; we silently ignore it
2518 return;
2519 }
2520 Persp3D *persp = *(sel_persps.begin());
2522 persp->tmat.set_infinite_direction (axis, adj->value);
2523 SP_OBJECT(persp)->updateRepr();
2525 // TODO: use the correct axis here, too
2526 sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2528 g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2529 }
2532 static void
2533 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2534 {
2535 box3d_angle_value_changed(adj, dataKludge, Proj::X);
2536 }
2538 static void
2539 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2540 {
2541 box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2542 }
2544 static void
2545 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2546 {
2547 box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2548 }
2551 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2552 {
2553 // TODO: Take all selected perspectives into account
2554 std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps();
2555 if (sel_persps.empty()) {
2556 // this can happen when the document is created; we silently ignore it
2557 return;
2558 }
2559 Persp3D *persp = *(sel_persps.begin());
2561 bool set_infinite = gtk_toggle_action_get_active(act);
2562 persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2563 }
2565 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2566 {
2567 box3d_vp_state_changed(act, box3d_angle, Proj::X);
2568 }
2570 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2571 {
2572 box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2573 }
2575 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2576 {
2577 box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2578 }
2580 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2581 {
2582 EgeAdjustmentAction* eact = 0;
2583 SPDocument *document = sp_desktop_document (desktop);
2584 Persp3D *persp = document->current_persp3d;
2586 EgeAdjustmentAction* box3d_angle_x = 0;
2587 EgeAdjustmentAction* box3d_angle_y = 0;
2588 EgeAdjustmentAction* box3d_angle_z = 0;
2590 /* Angle X */
2591 {
2592 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2593 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2594 eact = create_adjustment_action( "3DBoxAngleXAction",
2595 _("Angle in X direction"), _("Angle X:"),
2596 // Translators: PL is short for 'perspective line'
2597 _("Angle of PLs in X direction"),
2598 "tools.shapes.3dbox", "box3d_angle_x", 30,
2599 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2600 -360.0, 360.0, 1.0, 10.0,
2601 labels, values, G_N_ELEMENTS(labels),
2602 box3d_angle_x_value_changed );
2603 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2604 g_object_set_data( holder, "box3d_angle_x_action", eact );
2605 box3d_angle_x = eact;
2606 }
2608 if (!persp3d_VP_is_finite(persp, Proj::X)) {
2609 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2610 } else {
2611 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2612 }
2615 /* VP X state */
2616 {
2617 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2618 // Translators: VP is short for 'vanishing point'
2619 _("State of VP in X direction"),
2620 _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2621 "toggle_vp_x",
2622 Inkscape::ICON_SIZE_DECORATION );
2623 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2624 g_object_set_data( holder, "box3d_vp_x_state_action", act );
2625 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2626 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2627 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2628 }
2630 /* Angle Y */
2631 {
2632 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2633 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2634 eact = create_adjustment_action( "3DBoxAngleYAction",
2635 _("Angle in Y direction"), _("Angle Y:"),
2636 // Translators: PL is short for 'perspective line'
2637 _("Angle of PLs in Y direction"),
2638 "tools.shapes.3dbox", "box3d_angle_y", 30,
2639 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2640 -360.0, 360.0, 1.0, 10.0,
2641 labels, values, G_N_ELEMENTS(labels),
2642 box3d_angle_y_value_changed );
2643 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2644 g_object_set_data( holder, "box3d_angle_y_action", eact );
2645 box3d_angle_y = eact;
2646 }
2648 if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2649 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2650 } else {
2651 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2652 }
2654 /* VP Y state */
2655 {
2656 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2657 // Translators: VP is short for 'vanishing point'
2658 _("State of VP in Y direction"),
2659 _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2660 "toggle_vp_y",
2661 Inkscape::ICON_SIZE_DECORATION );
2662 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2663 g_object_set_data( holder, "box3d_vp_y_state_action", act );
2664 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
2665 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2666 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
2667 }
2669 /* Angle Z */
2670 {
2671 gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2672 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2673 eact = create_adjustment_action( "3DBoxAngleZAction",
2674 _("Angle in Z direction"), _("Angle Z:"),
2675 // Translators: PL is short for 'perspective line'
2676 _("Angle of PLs in Z direction"),
2677 "tools.shapes.3dbox", "box3d_angle_z", 30,
2678 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2679 -360.0, 360.0, 1.0, 10.0,
2680 labels, values, G_N_ELEMENTS(labels),
2681 box3d_angle_z_value_changed );
2682 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2683 g_object_set_data( holder, "box3d_angle_z_action", eact );
2684 box3d_angle_z = eact;
2685 }
2687 if (!persp3d_VP_is_finite(persp, Proj::Z)) {
2688 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2689 } else {
2690 gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2691 }
2693 /* VP Z state */
2694 {
2695 InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
2696 // Translators: VP is short for 'vanishing point'
2697 _("State of VP in Z direction"),
2698 _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
2699 "toggle_vp_z",
2700 Inkscape::ICON_SIZE_DECORATION );
2701 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2702 g_object_set_data( holder, "box3d_vp_z_state_action", act );
2703 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
2704 gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2705 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
2706 }
2708 sigc::connection *connection = new sigc::connection(
2709 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
2710 );
2711 g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
2712 g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
2713 }
2715 //########################
2716 //## Spiral ##
2717 //########################
2719 static void
2720 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
2721 {
2722 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2724 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2725 prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
2726 }
2728 // quit if run by the attr_changed listener
2729 if (g_object_get_data( tbl, "freeze" )) {
2730 return;
2731 }
2733 // in turn, prevent listener from responding
2734 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2736 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
2738 bool modmade = false;
2739 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
2740 items != NULL;
2741 items = items->next)
2742 {
2743 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2744 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2745 sp_repr_set_svg_double( repr, namespaced_name, adj->value );
2746 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
2747 modmade = true;
2748 }
2749 }
2751 g_free(namespaced_name);
2753 if (modmade) {
2754 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
2755 _("Change spiral"));
2756 }
2758 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2759 }
2761 static void
2762 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
2763 {
2764 sp_spl_tb_value_changed(adj, tbl, "revolution");
2765 }
2767 static void
2768 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
2769 {
2770 sp_spl_tb_value_changed(adj, tbl, "expansion");
2771 }
2773 static void
2774 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
2775 {
2776 sp_spl_tb_value_changed(adj, tbl, "t0");
2777 }
2779 static void
2780 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
2781 {
2782 GtkWidget *tbl = GTK_WIDGET(obj);
2784 GtkAdjustment *adj;
2786 // fixme: make settable
2787 gdouble rev = 5;
2788 gdouble exp = 1.0;
2789 gdouble t0 = 0.0;
2791 adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
2792 gtk_adjustment_set_value(adj, rev);
2793 gtk_adjustment_value_changed(adj);
2795 adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
2796 gtk_adjustment_set_value(adj, exp);
2797 gtk_adjustment_value_changed(adj);
2799 adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
2800 gtk_adjustment_set_value(adj, t0);
2801 gtk_adjustment_value_changed(adj);
2803 spinbutton_defocus(GTK_OBJECT(tbl));
2804 }
2807 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2808 gchar const */*old_value*/, gchar const */*new_value*/,
2809 bool /*is_interactive*/, gpointer data)
2810 {
2811 GtkWidget *tbl = GTK_WIDGET(data);
2813 // quit if run by the _changed callbacks
2814 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2815 return;
2816 }
2818 // in turn, prevent callbacks from responding
2819 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2821 GtkAdjustment *adj;
2822 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
2823 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
2825 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
2826 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
2828 adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
2829 gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
2831 g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2832 }
2835 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
2836 NULL, /* child_added */
2837 NULL, /* child_removed */
2838 spiral_tb_event_attr_changed,
2839 NULL, /* content_changed */
2840 NULL /* order_changed */
2841 };
2843 static void
2844 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2845 {
2846 int n_selected = 0;
2847 Inkscape::XML::Node *repr = NULL;
2849 purge_repr_listener( tbl, tbl );
2851 for (GSList const *items = selection->itemList();
2852 items != NULL;
2853 items = items->next)
2854 {
2855 if (SP_IS_SPIRAL((SPItem *) items->data)) {
2856 n_selected++;
2857 repr = SP_OBJECT_REPR((SPItem *) items->data);
2858 }
2859 }
2861 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2863 if (n_selected == 0) {
2864 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2865 } else if (n_selected == 1) {
2866 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2868 if (repr) {
2869 g_object_set_data( tbl, "repr", repr );
2870 Inkscape::GC::anchor(repr);
2871 sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
2872 sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
2873 }
2874 } else {
2875 // FIXME: implement averaging of all parameters for multiple selected
2876 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2877 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2878 }
2879 }
2882 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2883 {
2884 EgeAdjustmentAction* eact = 0;
2886 {
2887 EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
2888 ege_output_action_set_use_markup( act, TRUE );
2889 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2890 g_object_set_data( holder, "mode_action", act );
2891 }
2893 /* Revolution */
2894 {
2895 gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
2896 gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2897 eact = create_adjustment_action( "SpiralRevolutionAction",
2898 _("Number of turns"), _("Turns:"), _("Number of revolutions"),
2899 "tools.shapes.spiral", "revolution", 3.0,
2900 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
2901 0.01, 1024.0, 0.1, 1.0,
2902 labels, values, G_N_ELEMENTS(labels),
2903 sp_spl_tb_revolution_value_changed, 1, 2);
2904 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2905 }
2907 /* Expansion */
2908 {
2909 gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
2910 gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
2911 eact = create_adjustment_action( "SpiralExpansionAction",
2912 _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
2913 "tools.shapes.spiral", "expansion", 1.0,
2914 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2915 0.0, 1000.0, 0.01, 1.0,
2916 labels, values, G_N_ELEMENTS(labels),
2917 sp_spl_tb_expansion_value_changed);
2918 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2919 }
2921 /* T0 */
2922 {
2923 gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
2924 gdouble values[] = {0, 0.5, 0.9};
2925 eact = create_adjustment_action( "SpiralT0Action",
2926 _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
2927 "tools.shapes.spiral", "t0", 0.0,
2928 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2929 0.0, 0.999, 0.01, 1.0,
2930 labels, values, G_N_ELEMENTS(labels),
2931 sp_spl_tb_t0_value_changed);
2932 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2933 }
2935 /* Reset */
2936 {
2937 InkAction* inky = ink_action_new( "SpiralResetAction",
2938 _("Defaults"),
2939 _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2940 GTK_STOCK_CLEAR,
2941 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
2942 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
2943 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2944 }
2947 sigc::connection *connection = new sigc::connection(
2948 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
2949 );
2950 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2951 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2952 }
2954 //########################
2955 //## Pen/Pencil ##
2956 //########################
2959 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2960 {
2961 // Put stuff here
2962 }
2964 static void sp_pencil_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
2965 {
2966 // Put stuff here
2967 }
2969 //########################
2970 //## Tweak ##
2971 //########################
2973 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2974 {
2975 prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
2976 }
2978 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
2979 {
2980 prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
2981 }
2983 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
2984 {
2985 prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
2986 }
2988 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
2989 {
2990 int mode = ege_select_one_action_get_active( act );
2991 prefs_set_int_attribute("tools.tweak", "mode", mode);
2993 GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
2994 GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
2995 GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
2996 GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
2997 GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
2998 GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
2999 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3000 if (doh) gtk_action_set_sensitive (doh, TRUE);
3001 if (dos) gtk_action_set_sensitive (dos, TRUE);
3002 if (dol) gtk_action_set_sensitive (dol, TRUE);
3003 if (doo) gtk_action_set_sensitive (doo, TRUE);
3004 if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3005 if (fid) gtk_action_set_sensitive (fid, FALSE);
3006 } else {
3007 if (doh) gtk_action_set_sensitive (doh, FALSE);
3008 if (dos) gtk_action_set_sensitive (dos, FALSE);
3009 if (dol) gtk_action_set_sensitive (dol, FALSE);
3010 if (doo) gtk_action_set_sensitive (doo, FALSE);
3011 if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3012 if (fid) gtk_action_set_sensitive (fid, TRUE);
3013 }
3014 }
3016 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3017 {
3018 prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3019 }
3021 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3022 bool show = gtk_toggle_action_get_active( act );
3023 prefs_set_int_attribute ("tools.tweak", "doh", show ? 1 : 0);
3024 }
3025 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3026 bool show = gtk_toggle_action_get_active( act );
3027 prefs_set_int_attribute ("tools.tweak", "dos", show ? 1 : 0);
3028 }
3029 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3030 bool show = gtk_toggle_action_get_active( act );
3031 prefs_set_int_attribute ("tools.tweak", "dol", show ? 1 : 0);
3032 }
3033 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3034 bool show = gtk_toggle_action_get_active( act );
3035 prefs_set_int_attribute ("tools.tweak", "doo", show ? 1 : 0);
3036 }
3038 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3039 {
3040 {
3041 /* Width */
3042 gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3043 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3044 EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3045 _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3046 "tools.tweak", "width", 15,
3047 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3048 1, 100, 1.0, 10.0,
3049 labels, values, G_N_ELEMENTS(labels),
3050 sp_tweak_width_value_changed, 0.01, 0, 100 );
3051 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3052 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3053 }
3056 {
3057 /* Force */
3058 gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3059 gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3060 EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3061 _("Force"), _("Force:"), _("The force of the tweak action"),
3062 "tools.tweak", "force", 20,
3063 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3064 1, 100, 1.0, 10.0,
3065 labels, values, G_N_ELEMENTS(labels),
3066 sp_tweak_force_value_changed, 0.01, 0, 100 );
3067 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3068 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3069 }
3071 /* Mode */
3072 {
3073 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3075 GtkTreeIter iter;
3076 gtk_list_store_append( model, &iter );
3077 gtk_list_store_set( model, &iter,
3078 0, _("Push mode"),
3079 1, _("Push parts of paths in any direction"),
3080 2, "tweak_push_mode",
3081 -1 );
3083 gtk_list_store_append( model, &iter );
3084 gtk_list_store_set( model, &iter,
3085 0, _("Shrink mode"),
3086 1, _("Shrink (inset) parts of paths"),
3087 2, "tweak_shrink_mode",
3088 -1 );
3090 gtk_list_store_append( model, &iter );
3091 gtk_list_store_set( model, &iter,
3092 0, _("Grow mode"),
3093 1, _("Grow (outset) parts of paths"),
3094 2, "tweak_grow_mode",
3095 -1 );
3097 gtk_list_store_append( model, &iter );
3098 gtk_list_store_set( model, &iter,
3099 0, _("Attract mode"),
3100 1, _("Attract parts of paths towards cursor"),
3101 2, "tweak_attract_mode",
3102 -1 );
3104 gtk_list_store_append( model, &iter );
3105 gtk_list_store_set( model, &iter,
3106 0, _("Repel mode"),
3107 1, _("Repel parts of paths from cursor"),
3108 2, "tweak_repel_mode",
3109 -1 );
3111 gtk_list_store_append( model, &iter );
3112 gtk_list_store_set( model, &iter,
3113 0, _("Roughen mode"),
3114 1, _("Roughen parts of paths"),
3115 2, "tweak_roughen_mode",
3116 -1 );
3118 gtk_list_store_append( model, &iter );
3119 gtk_list_store_set( model, &iter,
3120 0, _("Color paint mode"),
3121 1, _("Paint the tool's color upon selected objects"),
3122 2, "tweak_colorpaint_mode",
3123 -1 );
3125 gtk_list_store_append( model, &iter );
3126 gtk_list_store_set( model, &iter,
3127 0, _("Color jitter mode"),
3128 1, _("Jitter the colors of selected objects"),
3129 2, "tweak_colorjitter_mode",
3130 -1 );
3132 EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3133 g_object_set( act, "short_label", _("Mode:"), NULL );
3134 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3135 g_object_set_data( holder, "mode_action", act );
3137 ege_select_one_action_set_appearance( act, "full" );
3138 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3139 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3140 ege_select_one_action_set_icon_column( act, 2 );
3141 ege_select_one_action_set_tooltip_column( act, 1 );
3143 gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3144 ege_select_one_action_set_active( act, mode );
3145 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3147 g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3148 }
3150 guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3152 {
3153 EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3154 ege_output_action_set_use_markup( act, TRUE );
3155 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3156 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3157 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3158 g_object_set_data( holder, "tweak_channels_label", act);
3159 }
3161 {
3162 InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3163 _("Hue"),
3164 _("In color mode, act on objects' hue"),
3165 NULL,
3166 Inkscape::ICON_SIZE_DECORATION );
3167 g_object_set( act, "short_label", _("H"), NULL );
3168 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3169 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3170 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3171 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3172 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3173 g_object_set_data( holder, "tweak_doh", act);
3174 }
3175 {
3176 InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3177 _("Saturation"),
3178 _("In color mode, act on objects' saturation"),
3179 NULL,
3180 Inkscape::ICON_SIZE_DECORATION );
3181 g_object_set( act, "short_label", _("S"), NULL );
3182 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3183 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3184 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3185 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3186 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3187 g_object_set_data( holder, "tweak_dos", act );
3188 }
3189 {
3190 InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3191 _("Lightness"),
3192 _("In color mode, act on objects' lightness"),
3193 NULL,
3194 Inkscape::ICON_SIZE_DECORATION );
3195 g_object_set( act, "short_label", _("L"), NULL );
3196 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3197 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3198 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3199 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3200 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3201 g_object_set_data( holder, "tweak_dol", act );
3202 }
3203 {
3204 InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3205 _("Opacity"),
3206 _("In color mode, act on objects' opacity"),
3207 NULL,
3208 Inkscape::ICON_SIZE_DECORATION );
3209 g_object_set( act, "short_label", _("O"), NULL );
3210 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3211 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3212 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3213 if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3214 gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3215 g_object_set_data( holder, "tweak_doo", act );
3216 }
3218 { /* Fidelity */
3219 gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3220 gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3221 EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3222 _("Fidelity"), _("Fidelity:"),
3223 _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3224 "tools.tweak", "fidelity", 50,
3225 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3226 1, 100, 1.0, 10.0,
3227 labels, values, G_N_ELEMENTS(labels),
3228 sp_tweak_fidelity_value_changed, 0.01, 0, 100 );
3229 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3230 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3231 if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3232 gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3233 g_object_set_data( holder, "tweak_fidelity", eact );
3234 }
3237 /* Use Pressure button */
3238 {
3239 InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3240 _("Pressure"),
3241 _("Use the pressure of the input device to alter the force of tweak action"),
3242 "use_pressure",
3243 Inkscape::ICON_SIZE_DECORATION );
3244 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3245 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3246 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3247 }
3249 }
3252 //########################
3253 //## Calligraphy ##
3254 //########################
3256 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3257 {
3258 prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value );
3259 }
3261 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3262 {
3263 prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value );
3264 }
3266 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3267 {
3268 prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3269 }
3271 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3272 {
3273 prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3274 }
3276 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3277 {
3278 prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value);
3279 }
3281 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3282 {
3283 prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value );
3284 }
3286 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3287 {
3288 prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value );
3289 }
3291 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* /*tbl*/ )
3292 {
3293 prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3294 }
3296 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3297 {
3298 prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3299 }
3301 static void sp_ddc_trace_background_changed( GtkToggleAction *act, gpointer /*data*/ )
3302 {
3303 prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3304 }
3306 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GtkAction *calligraphy_angle )
3307 {
3308 prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3310 gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3311 }
3313 static void sp_ddc_defaults(GtkWidget *, GObject *dataKludge)
3314 {
3315 // FIXME: make defaults settable via Inkscape Options
3316 struct KeyValue {
3317 char const *key;
3318 double value;
3319 } const key_values[] = {
3320 {"mass", 0.02},
3321 {"wiggle", 0.0},
3322 {"angle", 30.0},
3323 {"width", 15},
3324 {"thinning", 0.1},
3325 {"tremor", 0.0},
3326 {"flatness", 0.9},
3327 {"cap_rounding", 0.0}
3328 };
3330 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
3331 KeyValue const &kv = key_values[i];
3332 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
3333 if ( adj ) {
3334 gtk_adjustment_set_value(adj, kv.value);
3335 }
3336 }
3337 }
3340 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3341 {
3342 {
3343 EgeAdjustmentAction* calligraphy_angle = 0;
3345 {
3346 /* Width */
3347 gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
3348 gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3349 EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
3350 _("Pen Width"), _("Width:"),
3351 _("The width of the calligraphic pen (relative to the visible canvas area)"),
3352 "tools.calligraphic", "width", 15,
3353 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
3354 1, 100, 1.0, 10.0,
3355 labels, values, G_N_ELEMENTS(labels),
3356 sp_ddc_width_value_changed, 0.01, 0, 100 );
3357 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3358 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3359 }
3361 {
3362 /* Thinning */
3363 gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
3364 gdouble values[] = {-1, -0.4, -0.2, -0.1, 0, 0.1, 0.2, 0.4, 1};
3365 EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
3366 _("Stroke Thinning"), _("Thinning:"),
3367 _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
3368 "tools.calligraphic", "thinning", 0.1,
3369 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3370 -1.0, 1.0, 0.01, 0.1,
3371 labels, values, G_N_ELEMENTS(labels),
3372 sp_ddc_velthin_value_changed, 0.01, 2);
3373 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3374 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3375 }
3377 {
3378 /* Angle */
3379 gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
3380 gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3381 EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
3382 _("Pen Angle"), _("Angle:"),
3383 _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
3384 "tools.calligraphic", "angle", 30,
3385 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
3386 -90.0, 90.0, 1.0, 10.0,
3387 labels, values, G_N_ELEMENTS(labels),
3388 sp_ddc_angle_value_changed, 1, 0 );
3389 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3390 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3391 calligraphy_angle = eact;
3392 }
3394 {
3395 /* Fixation */
3396 gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
3397 gdouble values[] = {0, 0.2, 0.4, 0.6, 0.9, 1.0};
3398 EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
3399 _("Fixation"), _("Fixation:"),
3400 _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
3401 "tools.calligraphic", "flatness", 0.9,
3402 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3403 0.0, 1.0, 0.01, 0.1,
3404 labels, values, G_N_ELEMENTS(labels),
3405 sp_ddc_flatness_value_changed, 0.01, 2 );
3406 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3407 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3408 }
3410 {
3411 /* Cap Rounding */
3412 gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
3413 gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
3414 // TRANSLATORS: "cap" means "end" (both start and finish) here
3415 EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
3416 _("Cap rounding"), _("Caps:"),
3417 _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
3418 "tools.calligraphic", "cap_rounding", 0.0,
3419 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3420 0.0, 5.0, 0.01, 0.1,
3421 labels, values, G_N_ELEMENTS(labels),
3422 sp_ddc_cap_rounding_value_changed, 0.01, 2 );
3423 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3424 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3425 }
3427 {
3428 /* Tremor */
3429 gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
3430 gdouble values[] = {0, 0.1, 0.2, 0.4, 0.6, 1.0};
3431 EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
3432 _("Stroke Tremor"), _("Tremor:"),
3433 _("Increase to make strokes rugged and trembling"),
3434 "tools.calligraphic", "tremor", 0.0,
3435 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3436 0.0, 1.0, 0.01, 0.1,
3437 labels, values, G_N_ELEMENTS(labels),
3438 sp_ddc_tremor_value_changed, 0.01, 2 );
3440 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3441 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3442 }
3444 {
3445 /* Wiggle */
3446 gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
3447 gdouble values[] = {0, 0.2, 0.4, 0.6, 1.0};
3448 EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
3449 _("Pen Wiggle"), _("Wiggle:"),
3450 _("Increase to make the pen waver and wiggle"),
3451 "tools.calligraphic", "wiggle", 0.0,
3452 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3453 0.0, 1.0, 0.01, 0.1,
3454 labels, values, G_N_ELEMENTS(labels),
3455 sp_ddc_wiggle_value_changed, 0.01, 2 );
3456 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3457 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3458 }
3460 {
3461 /* Mass */
3462 gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
3463 gdouble values[] = {0.0, 0.02, 0.1, 0.2, 0.5, 1.0};
3464 EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
3465 _("Pen Mass"), _("Mass:"),
3466 _("Increase to make the pen drag behind, as if slowed by inertia"),
3467 "tools.calligraphic", "mass", 0.02,
3468 GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3469 0.0, 1.0, 0.01, 0.1,
3470 labels, values, G_N_ELEMENTS(labels),
3471 sp_ddc_mass_value_changed, 0.01, 2 );
3472 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3473 gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3474 }
3477 /* Trace Background button */
3478 {
3479 InkToggleAction* act = ink_toggle_action_new( "TraceAction",
3480 _("Trace Background"),
3481 _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
3482 "trace_background",
3483 Inkscape::ICON_SIZE_DECORATION );
3484 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3485 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), NULL);
3486 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
3487 }
3489 /* Use Pressure button */
3490 {
3491 InkToggleAction* act = ink_toggle_action_new( "PressureAction",
3492 _("Pressure"),
3493 _("Use the pressure of the input device to alter the width of the pen"),
3494 "use_pressure",
3495 Inkscape::ICON_SIZE_DECORATION );
3496 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3497 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), NULL);
3498 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
3499 }
3501 /* Use Tilt button */
3502 {
3503 InkToggleAction* act = ink_toggle_action_new( "TiltAction",
3504 _("Tilt"),
3505 _("Use the tilt of the input device to alter the angle of the pen's nib"),
3506 "use_tilt",
3507 Inkscape::ICON_SIZE_DECORATION );
3508 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3509 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), calligraphy_angle );
3510 gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3511 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
3512 }
3514 /* Reset */
3515 {
3516 GtkAction* act = gtk_action_new( "CalligraphyResetAction",
3517 _("Defaults"),
3518 _("Reset all parameters to defaults"),
3519 GTK_STOCK_CLEAR );
3520 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_ddc_defaults), holder );
3521 gtk_action_group_add_action( mainActions, act );
3522 gtk_action_set_sensitive( act, TRUE );
3523 }
3524 }
3525 }
3528 //########################
3529 //## Circle / Arc ##
3530 //########################
3532 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
3533 {
3534 GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
3535 GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
3537 if (v1 == 0 && v2 == 0) {
3538 if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
3539 gtk_action_set_sensitive( ocb, FALSE );
3540 gtk_action_set_sensitive( make_whole, FALSE );
3541 }
3542 } else {
3543 gtk_action_set_sensitive( ocb, TRUE );
3544 gtk_action_set_sensitive( make_whole, TRUE );
3545 }
3546 }
3548 static void
3549 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
3550 {
3551 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3553 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3554 prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
3555 }
3557 // quit if run by the attr_changed listener
3558 if (g_object_get_data( tbl, "freeze" )) {
3559 return;
3560 }
3562 // in turn, prevent listener from responding
3563 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3565 gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3567 bool modmade = false;
3568 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3569 items != NULL;
3570 items = items->next)
3571 {
3572 SPItem *item = SP_ITEM(items->data);
3574 if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
3576 SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
3577 SPArc *arc = SP_ARC(item);
3579 if (!strcmp(value_name, "start"))
3580 ge->start = (adj->value * M_PI)/ 180;
3581 else
3582 ge->end = (adj->value * M_PI)/ 180;
3584 sp_genericellipse_normalize(ge);
3585 ((SPObject *)arc)->updateRepr();
3586 ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
3588 modmade = true;
3589 }
3590 }
3592 g_free(namespaced_name);
3594 GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
3596 sp_arctb_sensitivize( tbl, adj->value, other->value );
3598 if (modmade) {
3599 sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
3600 _("Arc: Change start/end"));
3601 }
3603 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3604 }
3607 static void sp_arctb_start_value_changed(GtkAdjustment *adj, GObject *tbl)
3608 {
3609 sp_arctb_startend_value_changed(adj, tbl, "start", "end");
3610 }
3612 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
3613 {
3614 sp_arctb_startend_value_changed(adj, tbl, "end", "start");
3615 }
3617 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
3618 {
3619 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3620 if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3621 if ( ege_select_one_action_get_active( act ) != 0 ) {
3622 prefs_set_string_attribute("tools.shapes.arc", "open", "true");
3623 } else {
3624 prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
3625 }
3626 }
3628 // quit if run by the attr_changed listener
3629 if (g_object_get_data( tbl, "freeze" )) {
3630 return;
3631 }
3633 // in turn, prevent listener from responding
3634 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3636 bool modmade = false;
3638 if ( ege_select_one_action_get_active(act) != 0 ) {
3639 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3640 items != NULL;
3641 items = items->next)
3642 {
3643 if (SP_IS_ARC((SPItem *) items->data)) {
3644 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3645 repr->setAttribute("sodipodi:open", "true");
3646 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3647 modmade = true;
3648 }
3649 }
3650 } else {
3651 for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3652 items != NULL;
3653 items = items->next)
3654 {
3655 if (SP_IS_ARC((SPItem *) items->data)) {
3656 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3657 repr->setAttribute("sodipodi:open", NULL);
3658 SP_OBJECT((SPItem *) items->data)->updateRepr(repr, SP_OBJECT_WRITE_EXT);
3659 modmade = true;
3660 }
3661 }
3662 }
3664 if (modmade) {
3665 sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
3666 _("Arc: Change open/closed"));
3667 }
3669 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3670 }
3672 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
3673 {
3674 GtkAdjustment *adj;
3675 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
3676 gtk_adjustment_set_value(adj, 0.0);
3677 gtk_adjustment_value_changed(adj);
3679 adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
3680 gtk_adjustment_set_value(adj, 0.0);
3681 gtk_adjustment_value_changed(adj);
3683 spinbutton_defocus( GTK_OBJECT(obj) );
3684 }
3686 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3687 gchar const */*old_value*/, gchar const */*new_value*/,
3688 bool /*is_interactive*/, gpointer data)
3689 {
3690 GObject *tbl = G_OBJECT(data);
3692 // quit if run by the _changed callbacks
3693 if (g_object_get_data( tbl, "freeze" )) {
3694 return;
3695 }
3697 // in turn, prevent callbacks from responding
3698 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3700 gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
3701 gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
3703 GtkAdjustment *adj1,*adj2;
3704 adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
3705 gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
3706 adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
3707 gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
3709 sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
3711 char const *openstr = NULL;
3712 openstr = repr->attribute("sodipodi:open");
3713 EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
3715 if (openstr) {
3716 ege_select_one_action_set_active( ocb, 1 );
3717 } else {
3718 ege_select_one_action_set_active( ocb, 0 );
3719 }
3721 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3722 }
3724 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
3725 NULL, /* child_added */
3726 NULL, /* child_removed */
3727 arc_tb_event_attr_changed,
3728 NULL, /* content_changed */
3729 NULL /* order_changed */
3730 };
3733 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3734 {
3735 int n_selected = 0;
3736 Inkscape::XML::Node *repr = NULL;
3738 purge_repr_listener( tbl, tbl );
3740 for (GSList const *items = selection->itemList();
3741 items != NULL;
3742 items = items->next)
3743 {
3744 if (SP_IS_ARC((SPItem *) items->data)) {
3745 n_selected++;
3746 repr = SP_OBJECT_REPR((SPItem *) items->data);
3747 }
3748 }
3750 EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3752 g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3753 if (n_selected == 0) {
3754 g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3755 } else if (n_selected == 1) {
3756 g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3757 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3759 if (repr) {
3760 g_object_set_data( tbl, "repr", repr );
3761 Inkscape::GC::anchor(repr);
3762 sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
3763 sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
3764 }
3765 } else {
3766 // FIXME: implement averaging of all parameters for multiple selected
3767 //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3768 g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3769 sp_arctb_sensitivize( tbl, 1, 0 );
3770 }
3771 }
3774 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3775 {
3776 EgeAdjustmentAction* eact = 0;
3779 {
3780 EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
3781 ege_output_action_set_use_markup( act, TRUE );
3782 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3783 g_object_set_data( holder, "mode_action", act );
3784 }
3786 /* Start */
3787 {
3788 eact = create_adjustment_action( "ArcStartAction",
3789 _("Start"), _("Start:"),
3790 _("The angle (in degrees) from the horizontal to the arc's start point"),
3791 "tools.shapes.arc", "start", 0.0,
3792 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
3793 -360.0, 360.0, 1.0, 10.0,
3794 0, 0, 0,
3795 sp_arctb_start_value_changed);
3796 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3797 }
3799 /* End */
3800 {
3801 eact = create_adjustment_action( "ArcEndAction",
3802 _("End"), _("End:"),
3803 _("The angle (in degrees) from the horizontal to the arc's end point"),
3804 "tools.shapes.arc", "end", 0.0,
3805 GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3806 -360.0, 360.0, 1.0, 10.0,
3807 0, 0, 0,
3808 sp_arctb_end_value_changed);
3809 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3810 }
3812 /* Segments / Pie checkbox */
3813 {
3814 GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3816 GtkTreeIter iter;
3817 gtk_list_store_append( model, &iter );
3818 gtk_list_store_set( model, &iter,
3819 0, _("Closed arc"),
3820 1, _("Switch to segment (closed shape with two radii)"),
3821 2, "circle_closed_arc",
3822 -1 );
3824 gtk_list_store_append( model, &iter );
3825 gtk_list_store_set( model, &iter,
3826 0, _("Open Arc"),
3827 1, _("Switch to arc (unclosed shape)"),
3828 2, "circle_open_arc",
3829 -1 );
3831 EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
3832 gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3833 g_object_set_data( holder, "open_action", act );
3835 ege_select_one_action_set_appearance( act, "full" );
3836 ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3837 g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3838 ege_select_one_action_set_icon_column( act, 2 );
3839 ege_select_one_action_set_tooltip_column( act, 1 );
3841 gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
3842 bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
3843 ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
3844 g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
3845 }
3847 /* Make Whole */
3848 {
3849 InkAction* inky = ink_action_new( "ArcResetAction",
3850 _("Make whole"),
3851 _("Make the shape a whole ellipse, not arc or segment"),
3852 "reset_circle",
3853 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3854 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
3855 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3856 gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3857 g_object_set_data( holder, "make_whole", inky );
3858 }
3860 g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
3861 // sensitivize make whole and open checkbox
3862 {
3863 GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
3864 GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
3865 sp_arctb_sensitivize( holder, adj1->value, adj2->value );
3866 }
3869 sigc::connection *connection = new sigc::connection(
3870 sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
3871 );
3872 g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3873 g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3874 }
3879 // toggle button callbacks and updaters
3881 //########################
3882 //## Dropper ##
3883 //########################
3885 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
3886 prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
3887 GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
3888 if ( set_action ) {
3889 if ( gtk_toggle_action_get_active( act ) ) {
3890 gtk_action_set_sensitive( set_action, TRUE );
3891 } else {
3892 gtk_action_set_sensitive( set_action, FALSE );
3893 }
3894 }
3896 spinbutton_defocus(GTK_OBJECT(tbl));
3897 }
3899 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
3900 prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3901 spinbutton_defocus(GTK_OBJECT(tbl));
3902 }
3905 /**
3906 * Dropper auxiliary toolbar construction and setup.
3907 *
3908 * TODO: Would like to add swatch of current color.
3909 * TODO: Add queue of last 5 or so colors selected with new swatches so that
3910 * can drag and drop places. Will provide a nice mixing palette.
3911 */
3912 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3913 {
3914 gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
3916 {
3917 InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
3918 _("Pick alpha"),
3919 _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
3920 "color_alpha_get",
3921 Inkscape::ICON_SIZE_DECORATION );
3922 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3923 g_object_set_data( holder, "pick_action", act );
3924 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
3925 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
3926 }
3928 {
3929 InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
3930 _("Set alpha"),
3931 _("If alpha was picked, assign it to selection as fill or stroke transparency"),
3932 "color_alpha_set",
3933 Inkscape::ICON_SIZE_DECORATION );
3934 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3935 g_object_set_data( holder, "set_action", act );
3936 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
3937 // make sure it's disabled if we're not picking alpha
3938 gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
3939 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
3940 }
3941 }
3944 //########################
3945 //## Text Toolbox ##
3946 //########################
3947 /*
3948 static void
3949 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
3950 {
3951 //Call back for letter sizing spinbutton
3952 }
3954 static void
3955 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
3956 {
3957 //Call back for line height spinbutton
3958 }
3960 static void
3961 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3962 {
3963 //Call back for horizontal kerning spinbutton
3964 }
3966 static void
3967 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
3968 {
3969 //Call back for vertical kerning spinbutton
3970 }
3972 static void
3973 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
3974 {
3975 //Call back for letter rotation spinbutton
3976 }*/
3978 namespace {
3980 bool popdown_visible = false;
3981 bool popdown_hasfocus = false;
3983 void
3984 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
3985 {
3986 SPStyle *query =
3987 sp_style_new (SP_ACTIVE_DOCUMENT);
3989 int result_fontspec =
3990 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
3992 int result_family =
3993 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
3995 int result_style =
3996 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
3998 int result_numbers =
3999 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4001 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4003 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4004 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING)
4005 {
4006 Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
4008 if (repr)
4009 {
4010 sp_style_read_from_repr (query, repr);
4011 }
4012 else
4013 {
4014 return;
4015 }
4016 }
4018 if (query->text)
4019 {
4020 if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
4021 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4022 gtk_entry_set_text (GTK_ENTRY (entry), "");
4024 } else if (query->text->font_specification.value || query->text->font_family.value) {
4026 GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
4028 // Get the font that corresponds
4029 Glib::ustring familyName;
4031 font_instance * font = font_factory::Default()->FaceFromStyle(query);
4032 if (font) {
4033 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
4034 font->Unref();
4035 font = NULL;
4036 }
4038 gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
4040 Gtk::TreePath path;
4041 try {
4042 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
4043 } catch (...) {
4044 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
4045 return;
4046 }
4048 GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4049 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4051 g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
4053 gtk_tree_selection_select_path (tselection, path.gobj());
4054 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4056 g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
4057 }
4059 //Size
4060 GtkWidget *cbox = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4061 char *str = g_strdup_printf ("%.5g", query->font_size.computed);
4062 g_object_set_data (tbl, "size-block", gpointer(1));
4063 gtk_entry_set_text (GTK_ENTRY(GTK_BIN (cbox)->child), str);
4064 g_object_set_data (tbl, "size-block", gpointer(0));
4065 free (str);
4067 //Anchor
4068 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
4069 {
4070 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
4071 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4072 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4073 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4074 }
4075 else
4076 {
4077 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
4078 {
4079 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
4080 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4081 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4082 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4083 }
4084 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
4085 {
4086 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
4087 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4088 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4089 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4090 }
4091 else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
4092 {
4093 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
4094 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4095 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4096 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4097 }
4098 }
4100 //Style
4101 {
4102 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
4104 gboolean active = gtk_toggle_button_get_active (button);
4105 gboolean check = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
4107 if (active != check)
4108 {
4109 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4110 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4111 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4112 }
4113 }
4115 {
4116 GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
4118 gboolean active = gtk_toggle_button_get_active (button);
4119 gboolean check = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
4121 if (active != check)
4122 {
4123 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4124 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
4125 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4126 }
4127 }
4129 //Orientation
4130 //locking both buttons, changing one affect all group (both)
4131 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
4132 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
4134 GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
4135 g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
4137 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
4138 {
4139 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
4140 }
4141 else
4142 {
4143 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
4144 }
4145 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4146 g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
4147 }
4149 sp_style_unref(query);
4150 }
4152 void
4153 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4154 {
4155 sp_text_toolbox_selection_changed (selection, tbl);
4156 }
4158 void
4159 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
4160 {
4161 sp_text_toolbox_selection_changed (NULL, tbl);
4162 }
4164 void
4165 sp_text_toolbox_family_changed (GtkTreeSelection *selection,
4166 GObject *tbl)
4167 {
4168 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4169 GtkTreeModel *model = 0;
4170 GtkWidget *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4171 GtkTreeIter iter;
4172 char *family = 0;
4174 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4175 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4177 if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
4178 return;
4179 }
4181 gtk_tree_model_get (model, &iter, 0, &family, -1);
4183 if (g_object_get_data (G_OBJECT (selection), "block"))
4184 {
4185 gtk_entry_set_text (GTK_ENTRY (entry), family);
4186 return;
4187 }
4189 gtk_entry_set_text (GTK_ENTRY (entry), family);
4191 SPStyle *query =
4192 sp_style_new (SP_ACTIVE_DOCUMENT);
4194 int result_fontspec =
4195 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4197 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4199 SPCSSAttr *css = sp_repr_css_attr_new ();
4202 // First try to get the font spec from the stored value
4203 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4205 if (fontSpec.empty()) {
4206 // Construct a new font specification if it does not yet exist
4207 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4208 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4209 fontFromStyle->Unref();
4210 }
4212 if (!fontSpec.empty()) {
4213 Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
4214 if (!newFontSpec.empty() && fontSpec != newFontSpec) {
4215 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
4216 if (font) {
4217 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4219 // Set all the these just in case they were altered when finding the best
4220 // match for the new family and old style...
4222 gchar c[256];
4224 font->Family(c, 256);
4225 sp_repr_css_set_property (css, "font-family", c);
4227 font->Attribute( "weight", c, 256);
4228 sp_repr_css_set_property (css, "font-weight", c);
4230 font->Attribute("style", c, 256);
4231 sp_repr_css_set_property (css, "font-style", c);
4233 font->Attribute("stretch", c, 256);
4234 sp_repr_css_set_property (css, "font-stretch", c);
4236 font->Attribute("variant", c, 256);
4237 sp_repr_css_set_property (css, "font-variant", c);
4239 font->Unref();
4240 }
4241 }
4242 }
4244 // If querying returned nothing, set the default style of the tool (for new texts)
4245 if (result_fontspec == QUERY_STYLE_NOTHING)
4246 {
4247 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4248 sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
4249 }
4250 else
4251 {
4252 sp_desktop_set_style (desktop, css, true, true);
4253 }
4255 sp_style_unref(query);
4257 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4258 _("Text: Change font family"));
4259 sp_repr_css_attr_unref (css);
4260 free (family);
4261 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4263 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4264 }
4266 void
4267 sp_text_toolbox_family_entry_activate (GtkEntry *entry,
4268 GObject *tbl)
4269 {
4270 const char *family = gtk_entry_get_text (entry);
4272 try {
4273 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
4274 GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
4275 GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
4276 gtk_tree_selection_select_path (selection, path.gobj());
4277 gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
4278 gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4279 } catch (...) {
4280 if (family && strlen (family))
4281 {
4282 gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
4283 }
4284 }
4285 }
4287 void
4288 sp_text_toolbox_anchoring_toggled (GtkRadioButton *button,
4289 gpointer data)
4290 {
4291 if (g_object_get_data (G_OBJECT (button), "block")) return;
4292 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
4293 int prop = GPOINTER_TO_INT(data);
4295 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4296 SPCSSAttr *css = sp_repr_css_attr_new ();
4298 switch (prop)
4299 {
4300 case 0:
4301 {
4302 sp_repr_css_set_property (css, "text-anchor", "start");
4303 sp_repr_css_set_property (css, "text-align", "start");
4304 break;
4305 }
4306 case 1:
4307 {
4308 sp_repr_css_set_property (css, "text-anchor", "middle");
4309 sp_repr_css_set_property (css, "text-align", "center");
4310 break;
4311 }
4313 case 2:
4314 {
4315 sp_repr_css_set_property (css, "text-anchor", "end");
4316 sp_repr_css_set_property (css, "text-align", "end");
4317 break;
4318 }
4320 case 3:
4321 {
4322 sp_repr_css_set_property (css, "text-anchor", "start");
4323 sp_repr_css_set_property (css, "text-align", "justify");
4324 break;
4325 }
4326 }
4328 SPStyle *query =
4329 sp_style_new (SP_ACTIVE_DOCUMENT);
4330 int result_numbers =
4331 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4333 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4334 if (result_numbers == QUERY_STYLE_NOTHING)
4335 {
4336 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4337 }
4339 sp_style_unref(query);
4341 sp_desktop_set_style (desktop, css, true, true);
4342 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4343 _("Text: Change alignment"));
4344 sp_repr_css_attr_unref (css);
4346 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4347 }
4349 void
4350 sp_text_toolbox_style_toggled (GtkToggleButton *button,
4351 gpointer data)
4352 {
4353 if (g_object_get_data (G_OBJECT (button), "block")) return;
4355 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4356 SPCSSAttr *css = sp_repr_css_attr_new ();
4357 int prop = GPOINTER_TO_INT(data);
4358 bool active = gtk_toggle_button_get_active (button);
4360 SPStyle *query =
4361 sp_style_new (SP_ACTIVE_DOCUMENT);
4363 int result_fontspec =
4364 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
4366 int result_family =
4367 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
4369 int result_style =
4370 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
4372 int result_numbers =
4373 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4375 Glib::ustring fontSpec = query->text->font_specification.set ? query->text->font_specification.value : "";
4376 Glib::ustring newFontSpec = "";
4378 if (fontSpec.empty()) {
4379 // Construct a new font specification if it does not yet exist
4380 font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
4381 fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
4382 fontFromStyle->Unref();
4383 }
4385 switch (prop)
4386 {
4387 case 0:
4388 {
4389 if (!fontSpec.empty()) {
4390 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
4391 }
4392 if (fontSpec != newFontSpec) {
4393 // Don't even set the bold if the font didn't exist on the system
4394 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
4395 }
4396 break;
4397 }
4399 case 1:
4400 {
4401 if (!fontSpec.empty()) {
4402 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
4403 }
4404 if (fontSpec != newFontSpec) {
4405 // Don't even set the italic if the font didn't exist on the system
4406 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
4407 }
4408 break;
4409 }
4410 }
4412 if (!newFontSpec.empty()) {
4413 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
4414 }
4416 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4417 if (result_fontspec == QUERY_STYLE_NOTHING)
4418 {
4419 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4420 }
4422 sp_style_unref(query);
4424 sp_desktop_set_style (desktop, css, true, true);
4425 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4426 _("Text: Change font style"));
4427 sp_repr_css_attr_unref (css);
4429 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4430 }
4432 void
4433 sp_text_toolbox_orientation_toggled (GtkRadioButton *button,
4434 gpointer data)
4435 {
4436 if (g_object_get_data (G_OBJECT (button), "block")) {
4437 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
4438 return;
4439 }
4441 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4442 SPCSSAttr *css = sp_repr_css_attr_new ();
4443 int prop = GPOINTER_TO_INT(data);
4445 switch (prop)
4446 {
4447 case 0:
4448 {
4449 sp_repr_css_set_property (css, "writing-mode", "lr");
4450 break;
4451 }
4453 case 1:
4454 {
4455 sp_repr_css_set_property (css, "writing-mode", "tb");
4456 break;
4457 }
4458 }
4460 SPStyle *query =
4461 sp_style_new (SP_ACTIVE_DOCUMENT);
4462 int result_numbers =
4463 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4465 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4466 if (result_numbers == QUERY_STYLE_NOTHING)
4467 {
4468 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4469 }
4471 sp_desktop_set_style (desktop, css, true, true);
4472 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
4473 _("Text: Change orientation"));
4474 sp_repr_css_attr_unref (css);
4476 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4477 }
4479 gboolean
4480 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4481 {
4482 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4483 if (!desktop) return FALSE;
4485 switch (get_group0_keyval (event)) {
4486 case GDK_Escape: // defocus
4487 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4488 sp_text_toolbox_selection_changed (NULL, tbl); // update
4489 return TRUE; // I consumed the event
4490 break;
4491 }
4492 return FALSE;
4493 }
4495 gboolean
4496 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
4497 {
4498 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4499 if (!desktop) return FALSE;
4501 switch (get_group0_keyval (event)) {
4502 case GDK_KP_Enter:
4503 case GDK_Return:
4504 case GDK_Escape: // defocus
4505 gtk_widget_hide (w);
4506 popdown_visible = false;
4507 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4508 return TRUE; // I consumed the event
4509 break;
4510 case GDK_w:
4511 case GDK_W:
4512 if (event->state & GDK_CONTROL_MASK) {
4513 gtk_widget_hide (w);
4514 popdown_visible = false;
4515 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4516 return TRUE; // I consumed the event
4517 }
4518 break;
4519 }
4520 return FALSE;
4521 }
4524 void
4525 sp_text_toolbox_size_changed (GtkComboBox *cbox,
4526 GObject *tbl)
4527 {
4528 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4530 if (g_object_get_data (tbl, "size-block")) return;
4532 // If this is not from selecting a size in the list (in which case get_active will give the
4533 // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
4534 // process this event. This fixes GTK's stupid insistence on sending an activate change every
4535 // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
4536 if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
4537 return;
4539 gchar *endptr;
4540 gdouble value = -1;
4541 char *text = gtk_combo_box_get_active_text (cbox);
4542 if (text) {
4543 value = g_strtod (text, &endptr);
4544 if (endptr == text) // conversion failed, non-numeric input
4545 value = -1;
4546 free (text);
4547 }
4548 if (value <= 0) {
4549 return; // could not parse value
4550 }
4552 SPCSSAttr *css = sp_repr_css_attr_new ();
4553 Inkscape::CSSOStringStream osfs;
4554 osfs << value;
4555 sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
4557 SPStyle *query =
4558 sp_style_new (SP_ACTIVE_DOCUMENT);
4559 int result_numbers =
4560 sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
4562 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
4563 if (result_numbers == QUERY_STYLE_NOTHING)
4564 {
4565 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
4566 }
4568 sp_style_unref(query);
4570 sp_desktop_set_style (desktop, css, true, true);
4571 sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
4572 _("Text: Change font size"));
4573 sp_repr_css_attr_unref (css);
4575 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4576 }
4578 gboolean
4579 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
4580 {
4581 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4582 if (!desktop) return FALSE;
4584 if (!g_object_get_data (tbl, "esc-pressed")) {
4585 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4586 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4587 sp_text_toolbox_size_changed (cbox, tbl);
4588 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4589 }
4590 return FALSE; // I consumed the event
4591 }
4594 gboolean
4595 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
4596 {
4597 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4598 if (!desktop) return FALSE;
4600 switch (get_group0_keyval (event)) {
4601 case GDK_Escape: // defocus
4602 g_object_set_data (tbl, "esc-pressed", gpointer(1));
4603 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4604 g_object_set_data (tbl, "esc-pressed", gpointer(0));
4605 return TRUE; // I consumed the event
4606 break;
4607 case GDK_Return: // defocus
4608 case GDK_KP_Enter:
4609 g_object_set_data (tbl, "enter-pressed", gpointer(1));
4610 GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
4611 sp_text_toolbox_size_changed (cbox, tbl);
4612 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4613 g_object_set_data (tbl, "enter-pressed", gpointer(0));
4614 return TRUE; // I consumed the event
4615 break;
4616 }
4617 return FALSE;
4618 }
4620 void
4621 sp_text_toolbox_text_popdown_clicked (GtkButton */*button*/,
4622 GObject *tbl)
4623 {
4624 GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
4625 GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
4626 int x, y;
4628 if (!popdown_visible)
4629 {
4630 gdk_window_get_origin (widget->window, &x, &y);
4631 gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
4632 gtk_widget_show_all (popdown);
4633 //sp_transientize (popdown);
4635 gdk_pointer_grab (widget->window, TRUE,
4636 GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
4637 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
4638 GDK_POINTER_MOTION_MASK),
4639 NULL, NULL, GDK_CURRENT_TIME);
4641 gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
4643 popdown_visible = true;
4644 }
4645 else
4646 {
4647 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4648 gdk_pointer_ungrab (GDK_CURRENT_TIME);
4649 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
4650 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4651 gtk_widget_hide (popdown);
4652 popdown_visible = false;
4653 }
4654 }
4656 gboolean
4657 sp_text_toolbox_entry_focus_in (GtkWidget *entry,
4658 GdkEventFocus */*event*/,
4659 GObject */*tbl*/)
4660 {
4661 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
4662 return FALSE;
4663 }
4665 gboolean
4666 sp_text_toolbox_popdown_focus_out (GtkWidget *popdown,
4667 GdkEventFocus */*event*/,
4668 GObject */*tbl*/)
4669 {
4670 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
4672 if (popdown_hasfocus) {
4673 gtk_widget_hide (popdown);
4674 popdown_hasfocus = false;
4675 popdown_visible = false;
4676 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
4677 return TRUE;
4678 }
4679 return FALSE;
4680 }
4682 gboolean
4683 sp_text_toolbox_popdown_focus_in (GtkWidget */*popdown*/,
4684 GdkEventFocus */*event*/,
4685 GObject */*tbl*/)
4686 {
4687 popdown_hasfocus = true;
4688 return TRUE;
4689 }
4692 void
4693 cell_data_func (GtkTreeViewColumn */*column*/,
4694 GtkCellRenderer *cell,
4695 GtkTreeModel *tree_model,
4696 GtkTreeIter *iter,
4697 gpointer /*data*/)
4698 {
4699 char *family,
4700 *family_escaped,
4701 *sample_escaped;
4703 static const char *sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
4705 gtk_tree_model_get (tree_model, iter, 0, &family, -1);
4707 family_escaped = g_markup_escape_text (family, -1);
4708 sample_escaped = g_markup_escape_text (sample, -1);
4710 std::stringstream markup;
4711 markup << family_escaped << " <span foreground='darkgray' font_family='" << family_escaped << "'>" << sample_escaped << "</span>";
4712 g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
4714 free (family);
4715 free (family_escaped);
4716 free (sample_escaped);
4717 }
4719 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
4720 GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
4721 if (completion) {
4722 gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
4723 g_object_unref (completion);
4724 }
4725 }
4727 GtkWidget*
4728 sp_text_toolbox_new (SPDesktop *desktop)
4729 {
4730 GtkWidget *tbl = gtk_hbox_new (FALSE, 0);
4732 gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
4733 gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
4735 GtkTooltips *tt = gtk_tooltips_new();
4736 Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
4738 ////////////Family
4739 //Window
4740 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4741 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
4743 //Entry
4744 GtkWidget *entry = gtk_entry_new ();
4745 gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
4746 GtkEntryCompletion *completion = gtk_entry_completion_new ();
4747 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
4748 gtk_entry_completion_set_text_column (completion, 0);
4749 gtk_entry_completion_set_minimum_key_length (completion, 1);
4750 g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
4751 gtk_entry_set_completion (GTK_ENTRY(entry), completion);
4752 gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
4753 aux_toolbox_space (tbl, 1);
4754 gtk_box_pack_start (GTK_BOX (tbl), entry, FALSE, FALSE, 0);
4755 g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
4757 //Button
4758 GtkWidget *button = gtk_button_new ();
4759 gtk_container_add (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
4760 gtk_box_pack_start (GTK_BOX (tbl), button, FALSE, FALSE, 0);
4762 //Popdown
4763 GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
4764 GtkWidget *treeview = gtk_tree_view_new ();
4766 GtkCellRenderer *cell = gtk_cell_renderer_text_new ();
4767 GtkTreeViewColumn *column = gtk_tree_view_column_new ();
4768 gtk_tree_view_column_pack_start (column, cell, FALSE);
4769 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
4770 gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
4771 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
4773 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
4774 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
4775 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
4777 //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
4779 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
4780 gtk_container_add (GTK_CONTAINER (sw), treeview);
4782 gtk_container_add (GTK_CONTAINER (window), sw);
4783 gtk_widget_set_size_request (window, 300, 450);
4785 g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
4786 g_signal_connect (G_OBJECT (entry), "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
4787 g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
4789 g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
4791 g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
4792 g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
4793 g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
4795 GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
4796 g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
4798 g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
4799 g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
4800 g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
4801 g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
4802 g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
4804 GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
4805 aux_toolbox_space (tbl, 1);
4806 GtkWidget *box = gtk_event_box_new ();
4807 gtk_container_add (GTK_CONTAINER (box), image);
4808 gtk_box_pack_start (GTK_BOX (tbl), box, FALSE, FALSE, 4);
4809 g_object_set_data (G_OBJECT (tbl), "warning-image", box);
4810 GtkTooltips *tooltips = gtk_tooltips_new ();
4811 gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
4812 gtk_widget_hide (GTK_WIDGET (box));
4813 g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
4815 ////////////Size
4816 const char *sizes[] = {
4817 "4", "6", "8", "9", "10", "11", "12", "13", "14",
4818 "16", "18", "20", "22", "24", "28",
4819 "32", "36", "40", "48", "56", "64", "72", "144"
4820 };
4822 GtkWidget *cbox = gtk_combo_box_entry_new_text ();
4823 for (unsigned int n = 0; n < G_N_ELEMENTS (sizes); gtk_combo_box_append_text (GTK_COMBO_BOX(cbox), sizes[n++]));
4824 gtk_widget_set_size_request (cbox, 80, -1);
4825 aux_toolbox_space (tbl, 1);
4826 gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
4827 g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
4828 g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
4829 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
4830 gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
4832 //spacer
4833 aux_toolbox_space (tbl, 4);
4834 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4836 ////////////Text anchor
4837 GtkWidget *group = gtk_radio_button_new (NULL);
4838 GtkWidget *row = gtk_hbox_new (FALSE, 4);
4839 g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
4841 // left
4842 GtkWidget *rbutton = group;
4843 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4844 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4845 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4847 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4848 g_object_set_data (G_OBJECT (tbl), "text-start", rbutton);
4849 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
4850 gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
4852 // center
4853 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4854 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4855 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_SMALL_TOOLBAR));
4856 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4858 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4859 g_object_set_data (G_OBJECT (tbl), "text-middle", rbutton);
4860 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
4861 gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
4863 // right
4864 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4865 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4866 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_SMALL_TOOLBAR));
4867 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4869 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4870 g_object_set_data (G_OBJECT (tbl), "text-end", rbutton);
4871 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
4872 gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
4874 // fill
4875 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4876 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4877 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_SMALL_TOOLBAR));
4878 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4880 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4881 g_object_set_data (G_OBJECT (tbl), "text-fill", rbutton);
4882 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
4883 gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
4885 aux_toolbox_space (tbl, 1);
4886 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4888 //spacer
4889 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4891 ////////////Text style
4892 row = gtk_hbox_new (FALSE, 4);
4894 // bold
4895 rbutton = gtk_toggle_button_new ();
4896 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4897 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, GTK_ICON_SIZE_SMALL_TOOLBAR));
4898 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4899 gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
4901 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4902 g_object_set_data (G_OBJECT (tbl), "style-bold", rbutton);
4903 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
4905 // italic
4906 rbutton = gtk_toggle_button_new ();
4907 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4908 gtk_container_add (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, GTK_ICON_SIZE_SMALL_TOOLBAR));
4909 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4910 gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
4912 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4913 g_object_set_data (G_OBJECT (tbl), "style-italic", rbutton);
4914 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
4916 aux_toolbox_space (tbl, 1);
4917 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4919 //spacer
4920 gtk_box_pack_start (GTK_BOX (tbl), gtk_vseparator_new (), FALSE, FALSE, 4);
4922 ////////////Text orientation
4923 group = gtk_radio_button_new (NULL);
4924 row = gtk_hbox_new (FALSE, 4);
4925 g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
4927 // horizontal
4928 rbutton = group;
4929 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4930 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_LR));
4931 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4932 gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
4934 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4935 g_object_set_data (G_OBJECT (tbl), "orientation-horizontal", rbutton);
4936 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
4938 // vertical
4939 rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
4940 gtk_button_set_relief (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
4941 gtk_container_add (GTK_CONTAINER (rbutton), sp_icon_new (Inkscape::ICON_SIZE_SMALL_TOOLBAR, INKSCAPE_STOCK_WRITING_MODE_TB));
4942 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (rbutton), FALSE);
4943 gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
4945 gtk_box_pack_start (GTK_BOX (row), rbutton, FALSE, FALSE, 0);
4946 g_object_set_data (G_OBJECT (tbl), "orientation-vertical", rbutton);
4947 g_signal_connect (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
4948 gtk_box_pack_start (GTK_BOX (tbl), row, FALSE, FALSE, 4);
4951 //watch selection
4952 Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
4954 sigc::connection *c_selection_changed =
4955 new sigc::connection (sp_desktop_selection (desktop)->connectChanged
4956 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
4957 pool->add_connection ("selection-changed", c_selection_changed);
4959 sigc::connection *c_selection_modified =
4960 new sigc::connection (sp_desktop_selection (desktop)->connectModified
4961 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
4962 pool->add_connection ("selection-modified", c_selection_modified);
4964 sigc::connection *c_subselection_changed =
4965 new sigc::connection (desktop->connectToolSubselectionChanged
4966 (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
4967 pool->add_connection ("tool-subselection-changed", c_subselection_changed);
4969 Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
4972 gtk_widget_show_all (tbl);
4973 return tbl;
4975 } // end of sp_text_toolbox_new()
4977 }//<unnamed> namespace
4980 //#########################
4981 //## Connector ##
4982 //#########################
4984 static void sp_connector_path_set_avoid(void)
4985 {
4986 cc_selection_set_avoid(true);
4987 }
4990 static void sp_connector_path_set_ignore(void)
4991 {
4992 cc_selection_set_avoid(false);
4993 }
4997 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
4998 {
4999 // quit if run by the _changed callbacks
5000 if (g_object_get_data( tbl, "freeze" )) {
5001 return;
5002 }
5004 SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5005 SPDocument *doc = sp_desktop_document(desktop);
5007 if (!sp_document_get_undo_sensitive(doc))
5008 {
5009 return;
5010 }
5012 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5014 if ( repr->attribute("inkscape:connector-spacing") ) {
5015 gdouble priorValue = gtk_adjustment_get_value(adj);
5016 sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
5017 if ( priorValue == gtk_adjustment_get_value(adj) ) {
5018 return;
5019 }
5020 } else if ( adj->value == defaultConnSpacing ) {
5021 return;
5022 }
5024 // in turn, prevent callbacks from responding
5025 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5027 sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
5028 SP_OBJECT(desktop->namedview)->updateRepr();
5030 GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
5031 for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
5032 SPItem *item = reinterpret_cast<SPItem *>(iter->data);
5033 NR::Matrix m = NR::identity();
5034 avoid_item_move(&m, item);
5035 }
5037 if (items) {
5038 g_slist_free(items);
5039 }
5041 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
5042 _("Change connector spacing"));
5044 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5046 spinbutton_defocus(GTK_OBJECT(tbl));
5047 }
5049 static void sp_connector_graph_layout(void)
5050 {
5051 if (!SP_ACTIVE_DESKTOP) return;
5053 // hack for clones, see comment in align-and-distribute.cpp
5054 int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5055 prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
5057 graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
5059 prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
5061 sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
5062 }
5064 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5065 {
5066 if ( gtk_toggle_action_get_active( act ) ) {
5067 prefs_set_string_attribute("tools.connector", "directedlayout",
5068 "true");
5069 } else {
5070 prefs_set_string_attribute("tools.connector", "directedlayout",
5071 "false");
5072 }
5073 }
5075 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
5076 {
5077 if ( gtk_toggle_action_get_active( act ) ) {
5078 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5079 "true");
5080 } else {
5081 prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
5082 "false");
5083 }
5084 }
5087 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
5088 {
5089 prefs_set_double_attribute("tools.connector", "length", adj->value);
5090 spinbutton_defocus(GTK_OBJECT(tbl));
5091 }
5093 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
5094 gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
5095 bool /*is_interactive*/, gpointer data)
5096 {
5097 GtkWidget *tbl = GTK_WIDGET(data);
5099 if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5100 return;
5101 }
5102 if (strcmp(name, "inkscape:connector-spacing") != 0) {
5103 return;
5104 }
5106 GtkAdjustment *adj = (GtkAdjustment*)
5107 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
5108 gdouble spacing = defaultConnSpacing;
5109 sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
5111 gtk_adjustment_set_value(adj, spacing);
5112 }
5115 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
5116 NULL, /* child_added */
5117 NULL, /* child_removed */
5118 connector_tb_event_attr_changed,
5119 NULL, /* content_changed */
5120 NULL /* order_changed */
5121 };
5124 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
5125 {
5126 {
5127 InkAction* inky = ink_action_new( "ConnectorAvoidAction",
5128 _("Avoid"),
5129 _("Make connectors avoid selected objects"),
5130 "connector_avoid",
5131 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5132 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
5133 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5134 }
5136 {
5137 InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
5138 _("Ignore"),
5139 _("Make connectors ignore selected objects"),
5140 "connector_ignore",
5141 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5142 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
5143 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5144 }
5146 EgeAdjustmentAction* eact = 0;
5148 // Spacing spinbox
5149 eact = create_adjustment_action( "ConnectorSpacingAction",
5150 _("Connector Spacing"), _("Spacing:"),
5151 _("The amount of space left around objects by auto-routing connectors"),
5152 "tools.connector", "spacing", defaultConnSpacing,
5153 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
5154 0, 100, 1.0, 10.0,
5155 0, 0, 0,
5156 connector_spacing_changed, 1, 0 );
5157 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5159 // Graph (connector network) layout
5160 {
5161 InkAction* inky = ink_action_new( "ConnectorGraphAction",
5162 _("Graph"),
5163 _("Nicely arrange selected connector network"),
5164 "graph_layout",
5165 Inkscape::ICON_SIZE_SMALL_TOOLBAR );
5166 g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
5167 gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5168 }
5170 // Default connector length spinbox
5171 eact = create_adjustment_action( "ConnectorLengthAction",
5172 _("Connector Length"), _("Length:"),
5173 _("Ideal length for connectors when layout is applied"),
5174 "tools.connector", "length", 100,
5175 GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
5176 10, 1000, 10.0, 100.0,
5177 0, 0, 0,
5178 connector_length_changed, 1, 0 );
5179 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5182 // Directed edges toggle button
5183 {
5184 InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
5185 _("Downwards"),
5186 _("Make connectors with end-markers (arrows) point downwards"),
5187 "directed_graph",
5188 Inkscape::ICON_SIZE_DECORATION );
5189 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5191 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
5192 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5193 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5195 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
5196 }
5198 // Avoid overlaps toggle button
5199 {
5200 InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
5201 _("Remove overlaps"),
5202 _("Do not allow overlapping shapes"),
5203 "remove_overlaps",
5204 Inkscape::ICON_SIZE_DECORATION );
5205 gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5207 gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
5208 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
5209 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
5211 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
5212 }
5214 // Code to watch for changes to the connector-spacing attribute in
5215 // the XML.
5216 Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
5217 g_assert(repr != NULL);
5219 purge_repr_listener( holder, holder );
5221 if (repr) {
5222 g_object_set_data( holder, "repr", repr );
5223 Inkscape::GC::anchor(repr);
5224 sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
5225 sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
5226 }
5227 } // end of sp_connector_toolbox_prep()
5230 //#########################
5231 //## Paintbucket ##
5232 //#########################
5234 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
5235 {
5236 gint channels = ege_select_one_action_get_active( act );
5237 flood_channels_set_channels( channels );
5238 }
5240 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
5241 {
5242 prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
5243 }
5245 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
5246 {
5247 prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
5248 }
5250 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
5251 {
5252 UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
5253 SPUnit const *unit = tracker->getActiveUnit();
5255 prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
5257 prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
5258 }
5260 static void paintbucket_defaults(GtkWidget *, GObject *dataKludge)
5261 {
5262 // FIXME: make defaults settable via Inkscape Options
5263 struct KeyValue {
5264 char const *key;
5265 double value;
5266 } const key_values[] = {
5267 {"threshold", 15},
5268 {"offset", 0.0}
5269 };
5271 for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
5272 KeyValue const &kv = key_values[i];
5273 GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(dataKludge, kv.key));
5274 if ( adj ) {
5275 gtk_adjustment_set_value(adj, kv.value);
5276 }
5277 }
5279 EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "channels_action" ) );
5280 ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
5281 EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "autogap_action" ) );
5282 ege_select_one_action_set_active( autogap_action, 0 );
5283 }
5285 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5286 {
5287 EgeAdjustmentAction* eact = 0;
5289 {
5290 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5292 GList* items = 0;
5293 gint count = 0;
5294 for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
5295 {
5296 GtkTreeIter iter;
5297 gtk_list_store_append( model, &iter );
5298 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5299 count++;
5300 }
5301 g_list_free( items );
5302 items = 0;
5303 EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
5304 g_object_set( act1, "short_label", _("Fill by:"), NULL );
5305 ege_select_one_action_set_appearance( act1, "compact" );
5306 ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
5307 g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
5308 gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
5309 g_object_set_data( holder, "channels_action", act1 );
5310 }
5312 // Spacing spinbox
5313 {
5314 eact = create_adjustment_action(
5315 "ThresholdAction",
5316 _("Fill Threshold"), _("Threshold:"),
5317 _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
5318 "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
5319 "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
5320 0, 0, 0,
5321 paintbucket_threshold_changed, 1, 0 );
5323 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5324 }
5326 // Create the units menu.
5327 UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
5328 const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
5329 if (stored_unit)
5330 tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
5331 g_object_set_data( holder, "tracker", tracker );
5332 {
5333 GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
5334 gtk_action_group_add_action( mainActions, act );
5335 }
5337 // Offset spinbox
5338 {
5339 eact = create_adjustment_action(
5340 "OffsetAction",
5341 _("Grow/shrink by"), _("Grow/shrink by:"),
5342 _("The amount to grow (positive) or shrink (negative) the created fill path"),
5343 "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
5344 "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
5345 0, 0, 0,
5346 paintbucket_offset_changed, 1, 2);
5347 tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
5349 gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5350 }
5352 /* Auto Gap */
5353 {
5354 GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
5356 GList* items = 0;
5357 gint count = 0;
5358 for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
5359 {
5360 GtkTreeIter iter;
5361 gtk_list_store_append( model, &iter );
5362 gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
5363 count++;
5364 }
5365 g_list_free( items );
5366 items = 0;
5367 EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
5368 g_object_set( act2, "short_label", _("Close gaps:"), NULL );
5369 ege_select_one_action_set_appearance( act2, "compact" );
5370 ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
5371 g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
5372 gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
5373 g_object_set_data( holder, "autogap_action", act2 );
5374 }
5376 /* Reset */
5377 {
5378 GtkAction* act = gtk_action_new( "PaintbucketResetAction",
5379 _("Defaults"),
5380 _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
5381 GTK_STOCK_CLEAR );
5382 g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
5383 gtk_action_group_add_action( mainActions, act );
5384 gtk_action_set_sensitive( act, TRUE );
5385 }
5387 }
5389 /*
5390 Local Variables:
5391 mode:c++
5392 c-file-style:"stroustrup"
5393 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5394 indent-tabs-mode:nil
5395 fill-column:99
5396 End:
5397 */
5398 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :