Code

49cbbe0104366ab731e62b2ad3793504cc1fba71
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@users.sf.net>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *
16  * Copyright (C) 2004 David Turner
17  * Copyright (C) 2003 MenTaLguY
18  * Copyright (C) 1999-2008 authors
19  * Copyright (C) 2001-2002 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../node-context.h"
69 #include "../pen-context.h"
70 #include "../preferences.h"
71 #include "../selection-chemistry.h"
72 #include "../selection.h"
73 #include "select-toolbar.h"
74 #include "../shape-editor.h"
75 #include "../shortcuts.h"
76 #include "../sp-clippath.h"
77 #include "../sp-ellipse.h"
78 #include "../sp-flowtext.h"
79 #include "../sp-mask.h"
80 #include "../sp-namedview.h"
81 #include "../sp-rect.h"
82 #include "../sp-spiral.h"
83 #include "../sp-star.h"
84 #include "../sp-text.h"
85 #include "../style.h"
86 #include "../svg/css-ostringstream.h"
87 #include "../tools-switch.h"
88 #include "../tweak-context.h"
89 #include "../spray-context.h"
90 #include "../ui/dialog/calligraphic-profile-rename.h"
91 #include "../ui/icon-names.h"
92 #include "../ui/widget/style-swatch.h"
93 #include "../verbs.h"
94 #include "../widgets/button.h"
95 #include "../widgets/spinbutton-events.h"
96 #include "../widgets/spw-utilities.h"
97 #include "../widgets/widget-sizes.h"
98 #include "../xml/attribute-record.h"
99 #include "../xml/node-event-vector.h"
100 #include "../xml/repr.h"
102 #include "toolbox.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_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
123 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
131 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
132     static Inkscape::IconSize sizeChoices[] = {
133         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
134         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
135         Inkscape::ICON_SIZE_MENU
136     };
137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
138     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
139     return sizeChoices[index];
142 static struct {
143     gchar const *type_name;
144     gchar const *data_name;
145     sp_verb_t verb;
146     sp_verb_t doubleclick_verb;
147 } const tools[] = {
148     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
149     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
150     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
151     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
152     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
153     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
154     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
155     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
156     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
157     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
158     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
159     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
160     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
161     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
162     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
163     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
164     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
165     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
166     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
167     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
168     { NULL, NULL, 0, 0 }
169 };
171 static struct {
172     gchar const *type_name;
173     gchar const *data_name;
174     GtkWidget *(*create_func)(SPDesktop *desktop);
175     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
176     gchar const *ui_name;
177     gint swatch_verb_id;
178     gchar const *swatch_tool;
179     gchar const *swatch_tip;
180 } const aux_toolboxes[] = {
181     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
182       SP_VERB_INVALID, 0, 0},
183     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
184       SP_VERB_INVALID, 0, 0},
185     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
186       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
187     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
188       SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
189     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
190       SP_VERB_INVALID, 0, 0},
191     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
192       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
193     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
194       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
195     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
196       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
197     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
198       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
199     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
200       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
201     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
202       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
203     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
204       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
205     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
206       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
207     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
208       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
209     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
210       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
211     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
212       SP_VERB_INVALID, 0, 0},
213     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
214       SP_VERB_INVALID, 0, 0},
215     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
216       SP_VERB_INVALID, 0, 0},
217     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
218       SP_VERB_INVALID, 0, 0},
219     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
221     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227         "<ui>"
228         "  <toolbar name='SelectToolbar'>"
229         "    <toolitem action='EditSelectAll' />"
230         "    <toolitem action='EditSelectAllInAllLayers' />"
231         "    <toolitem action='EditDeselect' />"
232         "    <separator />"
233         "    <toolitem action='ObjectRotate90CCW' />"
234         "    <toolitem action='ObjectRotate90' />"
235         "    <toolitem action='ObjectFlipHorizontally' />"
236         "    <toolitem action='ObjectFlipVertically' />"
237         "    <separator />"
238         "    <toolitem action='SelectionToBack' />"
239         "    <toolitem action='SelectionLower' />"
240         "    <toolitem action='SelectionRaise' />"
241         "    <toolitem action='SelectionToFront' />"
242         "    <separator />"
243         "    <toolitem action='XAction' />"
244         "    <toolitem action='YAction' />"
245         "    <toolitem action='WidthAction' />"
246         "    <toolitem action='LockAction' />"
247         "    <toolitem action='HeightAction' />"
248         "    <toolitem action='UnitsAction' />"
249         "    <separator />"
250         "    <toolitem action='transform_affect_label' />"
251         "    <toolitem action='transform_stroke' />"
252         "    <toolitem action='transform_corners' />"
253         "    <toolitem action='transform_gradient' />"
254         "    <toolitem action='transform_pattern' />"
255         "  </toolbar>"
257         "  <toolbar name='NodeToolbar'>"
258         "    <toolitem action='NodeInsertAction' />"
259         "    <toolitem action='NodeDeleteAction' />"
260         "    <separator />"
261         "    <toolitem action='NodeJoinAction' />"
262         "    <toolitem action='NodeBreakAction' />"
263         "    <separator />"
264         "    <toolitem action='NodeJoinSegmentAction' />"
265         "    <toolitem action='NodeDeleteSegmentAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeCuspAction' />"
268         "    <toolitem action='NodeSmoothAction' />"
269         "    <toolitem action='NodeSymmetricAction' />"
270         "    <toolitem action='NodeAutoAction' />"
271         "    <separator />"
272         "    <toolitem action='NodeLineAction' />"
273         "    <toolitem action='NodeCurveAction' />"
274         "    <separator />"
275         "    <toolitem action='ObjectToPath' />"
276         "    <toolitem action='StrokeToPath' />"
277         "    <separator />"
278         "    <toolitem action='NodeXAction' />"
279         "    <toolitem action='NodeYAction' />"
280         "    <toolitem action='NodeUnitsAction' />"
281         "    <separator />"
282         "    <toolitem action='ObjectEditClipPathAction' />"
283         "    <toolitem action='ObjectEditMaskPathAction' />"
284         "    <toolitem action='EditNextLPEParameterAction' />"
285         "    <separator />"
286         "    <toolitem action='NodesShowHandlesAction' />"
287         "    <toolitem action='NodesShowHelperpath' />"
288         "  </toolbar>"
290         "  <toolbar name='TweakToolbar'>"
291         "    <toolitem action='TweakWidthAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakForceAction' />"
294         "    <toolitem action='TweakPressureAction' />"
295         "    <separator />"
296         "    <toolitem action='TweakModeAction' />"
297         "    <separator />"
298         "    <toolitem action='TweakFidelityAction' />"
299         "    <separator />"
300         "    <toolitem action='TweakChannelsLabel' />"
301         "    <toolitem action='TweakDoH' />"
302         "    <toolitem action='TweakDoS' />"
303         "    <toolitem action='TweakDoL' />"
304         "    <toolitem action='TweakDoO' />"
305         "  </toolbar>"
307         "  <toolbar name='SprayToolbar'>"
308         "    <toolitem action='SprayModeAction' />"
309         "    <separator />"
310         "    <separator />"
311         "    <toolitem action='SprayWidthAction' />"
312         "    <toolitem action='SprayPressureAction' />"
313         "    <toolitem action='SprayPopulationAction' />"
314         "    <separator />"
315         "    <toolitem action='SprayRotationAction' />"
316         "    <toolitem action='SprayScaleAction' />"
317         "    <separator />"
318         "    <toolitem action='SprayStandard_deviationAction' />"
319         "    <toolitem action='SprayMeanAction' />"
320         "  </toolbar>"
322         "  <toolbar name='ZoomToolbar'>"
323         "    <toolitem action='ZoomIn' />"
324         "    <toolitem action='ZoomOut' />"
325         "    <separator />"
326         "    <toolitem action='Zoom1:0' />"
327         "    <toolitem action='Zoom1:2' />"
328         "    <toolitem action='Zoom2:1' />"
329         "    <separator />"
330         "    <toolitem action='ZoomSelection' />"
331         "    <toolitem action='ZoomDrawing' />"
332         "    <toolitem action='ZoomPage' />"
333         "    <toolitem action='ZoomPageWidth' />"
334         "    <separator />"
335         "    <toolitem action='ZoomPrev' />"
336         "    <toolitem action='ZoomNext' />"
337         "  </toolbar>"
339         "  <toolbar name='StarToolbar'>"
340         "    <separator />"
341         "    <toolitem action='StarStateAction' />"
342         "    <separator />"
343         "    <toolitem action='FlatAction' />"
344         "    <separator />"
345         "    <toolitem action='MagnitudeAction' />"
346         "    <toolitem action='SpokeAction' />"
347         "    <toolitem action='RoundednessAction' />"
348         "    <toolitem action='RandomizationAction' />"
349         "    <separator />"
350         "    <toolitem action='StarResetAction' />"
351         "  </toolbar>"
353         "  <toolbar name='RectToolbar'>"
354         "    <toolitem action='RectStateAction' />"
355         "    <toolitem action='RectWidthAction' />"
356         "    <toolitem action='RectHeightAction' />"
357         "    <toolitem action='RadiusXAction' />"
358         "    <toolitem action='RadiusYAction' />"
359         "    <toolitem action='RectUnitsAction' />"
360         "    <separator />"
361         "    <toolitem action='RectResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='3DBoxToolbar'>"
365         "    <toolitem action='3DBoxAngleXAction' />"
366         "    <toolitem action='3DBoxVPXStateAction' />"
367         "    <separator />"
368         "    <toolitem action='3DBoxAngleYAction' />"
369         "    <toolitem action='3DBoxVPYStateAction' />"
370         "    <separator />"
371         "    <toolitem action='3DBoxAngleZAction' />"
372         "    <toolitem action='3DBoxVPZStateAction' />"
373         "  </toolbar>"
375         "  <toolbar name='SpiralToolbar'>"
376         "    <toolitem action='SpiralStateAction' />"
377         "    <toolitem action='SpiralRevolutionAction' />"
378         "    <toolitem action='SpiralExpansionAction' />"
379         "    <toolitem action='SpiralT0Action' />"
380         "    <separator />"
381         "    <toolitem action='SpiralResetAction' />"
382         "  </toolbar>"
384         "  <toolbar name='PenToolbar'>"
385         "    <toolitem action='FreehandModeActionPen' />"
386         "    <separator />"
387         "    <toolitem action='SetPenShapeAction'/>"
388         "  </toolbar>"
390         "  <toolbar name='PencilToolbar'>"
391         "    <toolitem action='FreehandModeActionPencil' />"
392         "    <separator />"
393         "    <toolitem action='PencilToleranceAction' />"
394         "    <separator />"
395         "    <toolitem action='PencilResetAction' />"
396         "    <separator />"
397         "    <toolitem action='SetPencilShapeAction'/>"
398         "  </toolbar>"
400         "  <toolbar name='CalligraphyToolbar'>"
401         "    <separator />"
402         "    <toolitem action='SetProfileAction'/>"
403         "    <separator />"
404         "    <toolitem action='CalligraphyWidthAction' />"
405         "    <toolitem action='PressureAction' />"
406         "    <toolitem action='TraceAction' />"
407         "    <toolitem action='ThinningAction' />"
408         "    <separator />"
409         "    <toolitem action='AngleAction' />"
410         "    <toolitem action='TiltAction' />"
411         "    <toolitem action='FixationAction' />"
412         "    <separator />"
413         "    <toolitem action='CapRoundingAction' />"
414         "    <separator />"
415         "    <toolitem action='TremorAction' />"
416         "    <toolitem action='WiggleAction' />"
417         "    <toolitem action='MassAction' />"
418         "    <separator />"
419         "  </toolbar>"
421         "  <toolbar name='ArcToolbar'>"
422         "    <toolitem action='ArcStateAction' />"
423         "    <separator />"
424         "    <toolitem action='ArcStartAction' />"
425         "    <toolitem action='ArcEndAction' />"
426         "    <separator />"
427         "    <toolitem action='ArcOpenAction' />"
428         "    <separator />"
429         "    <toolitem action='ArcResetAction' />"
430         "    <separator />"
431         "  </toolbar>"
433         "  <toolbar name='PaintbucketToolbar'>"
434         "    <toolitem action='ChannelsAction' />"
435         "    <separator />"
436         "    <toolitem action='ThresholdAction' />"
437         "    <separator />"
438         "    <toolitem action='OffsetAction' />"
439         "    <toolitem action='PaintbucketUnitsAction' />"
440         "    <separator />"
441         "    <toolitem action='AutoGapAction' />"
442         "    <separator />"
443         "    <toolitem action='PaintbucketResetAction' />"
444         "  </toolbar>"
446         "  <toolbar name='EraserToolbar'>"
447         "    <toolitem action='EraserWidthAction' />"
448         "    <separator />"
449         "    <toolitem action='EraserModeAction' />"
450         "  </toolbar>"
452         "  <toolbar name='LPEToolToolbar'>"
453         "    <toolitem action='LPEToolModeAction' />"
454         "    <separator />"
455         "    <toolitem action='LPEShowBBoxAction' />"
456         "    <toolitem action='LPEBBoxFromSelectionAction' />"
457         "    <separator />"
458         "    <toolitem action='LPELineSegmentAction' />"
459         "    <separator />"
460         "    <toolitem action='LPEMeasuringAction' />"
461         "    <toolitem action='LPEToolUnitsAction' />"
462         "    <separator />"
463         "    <toolitem action='LPEOpenLPEDialogAction' />"
464         "  </toolbar>"
466         "  <toolbar name='DropperToolbar'>"
467         "    <toolitem action='DropperOpacityAction' />"
468         "    <toolitem action='DropperPickAlphaAction' />"
469         "    <toolitem action='DropperSetAlphaAction' />"
470         "  </toolbar>"
472         "  <toolbar name='ConnectorToolbar'>"
473         "    <toolitem action='ConnectorEditModeAction' />"
474         "    <toolitem action='ConnectorAvoidAction' />"
475         "    <toolitem action='ConnectorIgnoreAction' />"
476         "    <toolitem action='ConnectorOrthogonalAction' />"
477         "    <toolitem action='ConnectorCurvatureAction' />"
478         "    <toolitem action='ConnectorSpacingAction' />"
479         "    <toolitem action='ConnectorGraphAction' />"
480         "    <toolitem action='ConnectorLengthAction' />"
481         "    <toolitem action='ConnectorDirectedAction' />"
482         "    <toolitem action='ConnectorOverlapAction' />"
483         "    <toolitem action='ConnectorNewConnPointAction' />"
484         "    <toolitem action='ConnectorRemoveConnPointAction' />"
485         "  </toolbar>"
487         "</ui>"
490 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
492 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func);
494 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
495 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
497 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
498 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
500 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
501 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
503 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
504                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
505                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
507 class VerbAction : public Gtk::Action {
508 public:
509     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511     virtual ~VerbAction();
512     virtual void set_active(bool active = true);
514 protected:
515     virtual Gtk::Widget* create_menu_item_vfunc();
516     virtual Gtk::Widget* create_tool_item_vfunc();
518     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
519     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
521     virtual void on_activate();
523 private:
524     Inkscape::Verb* verb;
525     Inkscape::Verb* verb2;
526     Inkscape::UI::View::View *view;
527     GtkTooltips *tooltips;
528     bool active;
530     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
531 };
534 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
536     Glib::RefPtr<VerbAction> result;
537     SPAction *action = verb->get_action(view);
538     if ( action ) {
539         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
540         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
541     }
543     return result;
546 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
547     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
548     verb(verb),
549     verb2(verb2),
550     view(view),
551     tooltips(tooltips),
552     active(false)
556 VerbAction::~VerbAction()
560 Gtk::Widget* VerbAction::create_menu_item_vfunc()
562 // First call in to get the icon rendered if present in SVG
563     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
564     delete widget;
565     widget = 0;
567     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
568 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
569     return widg;
572 Gtk::Widget* VerbAction::create_tool_item_vfunc()
574 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
575     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
576     GtkWidget* toolbox = 0;
577     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
578                                                                           SP_BUTTON_TYPE_TOGGLE,
579                                                                           verb,
580                                                                           verb2,
581                                                                           view,
582                                                                           tooltips );
583     if ( active ) {
584         sp_button_toggle_set_down( SP_BUTTON(button), active);
585     }
586     gtk_widget_show_all( button );
587     Gtk::Widget* wrapped = Glib::wrap(button);
588     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
589     holder->add(*wrapped);
591 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
592     return holder;
595 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
597 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
598     Gtk::Action::connect_proxy_vfunc(proxy);
601 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
603 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
604     Gtk::Action::disconnect_proxy_vfunc(proxy);
607 void VerbAction::set_active(bool active)
609     this->active = active;
610     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
611     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
612         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
613         if (ti) {
614             // *should* have one child that is the SPButton
615             Gtk::Widget* child = ti->get_child();
616             if ( child && SP_IS_BUTTON(child->gobj()) ) {
617                 SPButton* button = SP_BUTTON(child->gobj());
618                 sp_button_toggle_set_down( button, active );
619             }
620         }
621     }
624 void VerbAction::on_activate()
626     if ( verb ) {
627         SPAction *action = verb->get_action(view);
628         if ( action ) {
629             sp_action_perform(action, 0);
630         }
631     }
634 /* Global text entry widgets necessary for update */
635 /* GtkWidget *dropper_rgb_entry,
636           *dropper_opacity_entry ; */
637 // should be made a private member once this is converted to class
639 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
640     connection->disconnect();
641     delete connection;
644 static void purge_repr_listener( GObject* obj, GObject* tbl )
646     (void)obj;
647     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
648     if (oldrepr) { // remove old listener
649         sp_repr_remove_listener_by_data(oldrepr, tbl);
650         Inkscape::GC::release(oldrepr);
651         oldrepr = 0;
652         g_object_set_data( tbl, "repr", NULL );
653     }
656 GtkWidget *
657 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
658                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
659                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
661     SPAction *action = verb->get_action(view);
662     if (!action) return NULL;
664     SPAction *doubleclick_action;
665     if (doubleclick_verb)
666         doubleclick_action = doubleclick_verb->get_action(view);
667     else
668         doubleclick_action = NULL;
670     /* fixme: Handle sensitive/unsensitive */
671     /* fixme: Implement sp_button_new_from_action */
672     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
673     gtk_widget_show(b);
676     unsigned int shortcut = sp_shortcut_get_primary(verb);
677     if (shortcut) {
678         gchar key[256];
679         sp_ui_shortcut_string(shortcut, key);
680         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
681         if ( t ) {
682             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
683         }
684         g_free(tip);
685     } else {
686         if ( t ) {
687             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
688         }
689     }
691     return b;
695 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
697     SPAction* targetAction = SP_ACTION(user_data);
698     if ( targetAction ) {
699         sp_action_perform( targetAction, NULL );
700     }
703 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
705     if ( data ) {
706         GtkAction* act = GTK_ACTION(data);
707         gtk_action_set_sensitive( act, sensitive );
708     }
711 static SPActionEventVector action_event_vector = {
712     {NULL},
713     NULL,
714     NULL,
715     sp_action_action_set_sensitive,
716     NULL,
717     NULL
718 };
720 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
722     GtkAction* act = 0;
724     SPAction* targetAction = verb->get_action(view);
725     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
726     act = GTK_ACTION(inky);
727     gtk_action_set_sensitive( act, targetAction->sensitive );
729     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
731     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
732     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
734     return act;
737 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
739     Inkscape::UI::View::View *view = desktop;
740     gint verbsToUse[] = {
741         // disabled until we have icons for them:
742         //find
743         //SP_VERB_EDIT_TILE,
744         //SP_VERB_EDIT_UNTILE,
745         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
746         SP_VERB_DIALOG_DISPLAY,
747         SP_VERB_DIALOG_FILL_STROKE,
748         SP_VERB_DIALOG_NAMEDVIEW,
749         SP_VERB_DIALOG_TEXT,
750         SP_VERB_DIALOG_XML_EDITOR,
751         SP_VERB_DIALOG_LAYERS,
752         SP_VERB_EDIT_CLONE,
753         SP_VERB_EDIT_COPY,
754         SP_VERB_EDIT_CUT,
755         SP_VERB_EDIT_DUPLICATE,
756         SP_VERB_EDIT_PASTE,
757         SP_VERB_EDIT_REDO,
758         SP_VERB_EDIT_UNDO,
759         SP_VERB_EDIT_UNLINK_CLONE,
760         SP_VERB_FILE_EXPORT,
761         SP_VERB_FILE_IMPORT,
762         SP_VERB_FILE_NEW,
763         SP_VERB_FILE_OPEN,
764         SP_VERB_FILE_PRINT,
765         SP_VERB_FILE_SAVE,
766         SP_VERB_OBJECT_TO_CURVE,
767         SP_VERB_SELECTION_GROUP,
768         SP_VERB_SELECTION_OUTLINE,
769         SP_VERB_SELECTION_UNGROUP,
770         SP_VERB_ZOOM_1_1,
771         SP_VERB_ZOOM_1_2,
772         SP_VERB_ZOOM_2_1,
773         SP_VERB_ZOOM_DRAWING,
774         SP_VERB_ZOOM_IN,
775         SP_VERB_ZOOM_NEXT,
776         SP_VERB_ZOOM_OUT,
777         SP_VERB_ZOOM_PAGE,
778         SP_VERB_ZOOM_PAGE_WIDTH,
779         SP_VERB_ZOOM_PREV,
780         SP_VERB_ZOOM_SELECTION,
781     };
783     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
785     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
786     Glib::RefPtr<Gtk::ActionGroup> mainActions;
787     if ( groups.find(desktop) != groups.end() ) {
788         mainActions = groups[desktop];
789     }
791     if ( !mainActions ) {
792         mainActions = Gtk::ActionGroup::create("main");
793         groups[desktop] = mainActions;
794     }
796     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
797         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
798         if ( verb ) {
799             if (!mainActions->get_action(verb->get_id())) {
800                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
801                 mainActions->add(Glib::wrap(act));
802             }
803         }
804     }
806     if ( !mainActions->get_action("ToolZoom") ) {
807         GtkTooltips *tt = gtk_tooltips_new();
808         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
809             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
810             if ( va ) {
811                 mainActions->add(va);
812                 if ( i == 0 ) {
813                     va->set_active(true);
814                 }
815             }
816         }
817     }
820     return mainActions;
824 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
826     gtk_widget_set_size_request( widget,
827                                  widget->allocation.width,
828                                  widget->allocation.height );
831 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
833     gtk_widget_set_size_request( widget, -1, -1 );
838 static GtkWidget* toolboxNewCommon( GtkWidget* tb, GtkPositionType handlePos )
840     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
842     gtk_widget_set_sensitive(tb, FALSE);
844     GtkWidget *hb = gtk_handle_box_new();
845     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
846     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
847     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
849     gtk_container_add(GTK_CONTAINER(hb), tb);
850     gtk_widget_show(GTK_WIDGET(tb));
852     sigc::connection* conn = new sigc::connection;
853     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
855     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
856     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
858     return hb;
861 GtkWidget *sp_tool_toolbox_new()
863     GtkWidget *toolBar = gtk_toolbar_new();
864     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
865     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
867     return toolboxNewCommon( toolBar, GTK_POS_TOP );
870 GtkWidget *sp_aux_toolbox_new()
872     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
874     g_object_set_data(G_OBJECT(tb), "MarkForChild", const_cast<gchar *>("MarkForChild"));
876     return toolboxNewCommon( tb, GTK_POS_LEFT );
879 //####################################
880 //# Commands Bar
881 //####################################
883 GtkWidget *sp_commands_toolbox_new()
885     GtkWidget *tb = gtk_toolbar_new();
887     return toolboxNewCommon( tb, GTK_POS_LEFT );
890 GtkWidget *sp_snap_toolbox_new()
892     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
894     return toolboxNewCommon( tb, GTK_POS_LEFT );
897 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
898                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
899                                                        Glib::ustring const &path, gdouble def,
900                                                        GtkWidget *focusTarget,
901                                                        GtkWidget *us,
902                                                        GObject *dataKludge,
903                                                        gboolean altx, gchar const *altx_mark,
904                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
905                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
906                                                        void (*callback)(GtkAdjustment *, GObject *),
907                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
909     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
910     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
911                                                              lower, upper, step, page, 0 ) );
912     if (us) {
913         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
914     }
916     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
918     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
919     if ( shortLabel ) {
920         g_object_set( act, "short_label", shortLabel, NULL );
921     }
923     if ( (descrCount > 0) && descrLabels && descrValues ) {
924         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
925     }
927     if ( focusTarget ) {
928         ege_adjustment_action_set_focuswidget( act, focusTarget );
929     }
931     if ( altx && altx_mark ) {
932         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
933     }
935     if ( dataKludge ) {
936         // Rather lame, but it's the only place where we need to get the entry name
937         // but we don't have an Entry
938         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
939     }
941     // Using a cast just to make sure we pass in the right kind of function pointer
942     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
944     return act;
948 //####################################
949 //# node editing callbacks
950 //####################################
952 /**
953  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
954  */
955 static ShapeEditor *get_current_shape_editor()
957     if (!SP_ACTIVE_DESKTOP) {
958         return NULL;
959     }
961     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
963     if (!SP_IS_NODE_CONTEXT(event_context)) {
964         return NULL;
965     }
967     return event_context->shape_editor;
971 void
972 sp_node_path_edit_add(void)
974     ShapeEditor *shape_editor = get_current_shape_editor();
975     if (shape_editor) shape_editor->add_node();
978 void
979 sp_node_path_edit_delete(void)
981     ShapeEditor *shape_editor = get_current_shape_editor();
982     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
985 void
986 sp_node_path_edit_delete_segment(void)
988     ShapeEditor *shape_editor = get_current_shape_editor();
989     if (shape_editor) shape_editor->delete_segment();
992 void
993 sp_node_path_edit_break(void)
995     ShapeEditor *shape_editor = get_current_shape_editor();
996     if (shape_editor) shape_editor->break_at_nodes();
999 void
1000 sp_node_path_edit_join(void)
1002     ShapeEditor *shape_editor = get_current_shape_editor();
1003     if (shape_editor) shape_editor->join_nodes();
1006 void
1007 sp_node_path_edit_join_segment(void)
1009     ShapeEditor *shape_editor = get_current_shape_editor();
1010     if (shape_editor) shape_editor->join_segments();
1013 void
1014 sp_node_path_edit_toline(void)
1016     ShapeEditor *shape_editor = get_current_shape_editor();
1017     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1020 void
1021 sp_node_path_edit_tocurve(void)
1023     ShapeEditor *shape_editor = get_current_shape_editor();
1024     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1027 void
1028 sp_node_path_edit_cusp(void)
1030     ShapeEditor *shape_editor = get_current_shape_editor();
1031     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1034 void
1035 sp_node_path_edit_smooth(void)
1037     ShapeEditor *shape_editor = get_current_shape_editor();
1038     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1041 void
1042 sp_node_path_edit_symmetrical(void)
1044     ShapeEditor *shape_editor = get_current_shape_editor();
1045     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1048 void
1049 sp_node_path_edit_auto(void)
1051     ShapeEditor *shape_editor = get_current_shape_editor();
1052     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1055 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1056     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1057     bool show = gtk_toggle_action_get_active( act );
1058     prefs->setBool("/tools/nodes/show_handles",  show);
1059     ShapeEditor *shape_editor = get_current_shape_editor();
1060     if (shape_editor) shape_editor->show_handles(show);
1063 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1064     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1065     bool show = gtk_toggle_action_get_active( act );
1066     prefs->setBool("/tools/nodes/show_helperpath",  show);
1067     ShapeEditor *shape_editor = get_current_shape_editor();
1068     if (shape_editor) shape_editor->show_helperpath(show);
1071 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1072     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1075 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1076     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1079 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1080     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1083 /* is called when the node selection is modified */
1084 static void
1085 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1087     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1088     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1089     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1090     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1092     // quit if run by the attr_changed listener
1093     if (g_object_get_data( tbl, "freeze" )) {
1094         return;
1095     }
1097     // in turn, prevent listener from responding
1098     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1100     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1101     SPUnit const *unit = tracker->getActiveUnit();
1103     ShapeEditor *shape_editor = get_current_shape_editor();
1104     if (shape_editor && shape_editor->has_nodepath()) {
1105         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1106         int n_selected = 0;
1107         if (nodepath) {
1108             n_selected = nodepath->numSelected();
1109         }
1111         if (n_selected == 0) {
1112             gtk_action_set_sensitive(xact, FALSE);
1113             gtk_action_set_sensitive(yact, FALSE);
1114         } else {
1115             gtk_action_set_sensitive(xact, TRUE);
1116             gtk_action_set_sensitive(yact, TRUE);
1117             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1118             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1120             if (n_selected == 1) {
1121                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1122                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1123                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1124                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1125                 }
1126             } else {
1127                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1128                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1129                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1130                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1131                        selected nodes don't coincide (in this case we use the coordinates of the center
1132                        of the bounding box). So the entries are never set to zero. */
1133                     // FIXME: Maybe we should clear the entry if several nodes are selected
1134                     //        instead of providing a kind of average value
1135                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1136                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1137                 }
1138             }
1139         }
1140     } else {
1141         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1142         gtk_action_set_sensitive(xact, FALSE);
1143         gtk_action_set_sensitive(yact, FALSE);
1144     }
1146     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1149 static void
1150 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1152     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1153     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1155     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1156     SPUnit const *unit = tracker->getActiveUnit();
1158     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1159         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1160     }
1162     // quit if run by the attr_changed listener
1163     if (g_object_get_data( tbl, "freeze" )) {
1164         return;
1165     }
1167     // in turn, prevent listener from responding
1168     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1170     ShapeEditor *shape_editor = get_current_shape_editor();
1171     if (shape_editor && shape_editor->has_nodepath()) {
1172         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1173         if (!strcmp(value_name, "x")) {
1174             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1175         }
1176         if (!strcmp(value_name, "y")) {
1177             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1178         }
1179     }
1181     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1184 static void
1185 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1187     sp_node_path_value_changed(adj, tbl, "x");
1190 static void
1191 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1193     sp_node_path_value_changed(adj, tbl, "y");
1196 void
1197 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1199     {
1200     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1201     SPItem *item = selection->singleItem();
1202     if (item && SP_IS_LPE_ITEM(item)) {
1203        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1204            gtk_action_set_sensitive(w, TRUE);
1205        } else {
1206            gtk_action_set_sensitive(w, FALSE);
1207        }
1208     } else {
1209        gtk_action_set_sensitive(w, FALSE);
1210     }
1211     }
1213     {
1214     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1215     SPItem *item = selection->singleItem();
1216     if (item && item->clip_ref && item->clip_ref->getObject()) {
1217        gtk_action_set_sensitive(w, TRUE);
1218     } else {
1219        gtk_action_set_sensitive(w, FALSE);
1220     }
1221     }
1223     {
1224     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1225     SPItem *item = selection->singleItem();
1226     if (item && item->mask_ref && item->mask_ref->getObject()) {
1227        gtk_action_set_sensitive(w, TRUE);
1228     } else {
1229        gtk_action_set_sensitive(w, FALSE);
1230     }
1231     }
1234 void
1235 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1237     sp_node_toolbox_sel_changed (selection, tbl);
1242 //################################
1243 //##    Node Editing Toolbox    ##
1244 //################################
1246 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1248     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1249     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1250     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1251     g_object_set_data( holder, "tracker", tracker );
1253     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1255     {
1256         InkAction* inky = ink_action_new( "NodeInsertAction",
1257                                           _("Insert node"),
1258                                           _("Insert new nodes into selected segments"),
1259                                           INKSCAPE_ICON_NODE_ADD,
1260                                           secondarySize );
1261         g_object_set( inky, "short_label", _("Insert"), NULL );
1262         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1263         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1264     }
1266     {
1267         InkAction* inky = ink_action_new( "NodeDeleteAction",
1268                                           _("Delete node"),
1269                                           _("Delete selected nodes"),
1270                                           INKSCAPE_ICON_NODE_DELETE,
1271                                           secondarySize );
1272         g_object_set( inky, "short_label", _("Delete"), NULL );
1273         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1274         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1275     }
1277     {
1278         InkAction* inky = ink_action_new( "NodeJoinAction",
1279                                           _("Join endnodes"),
1280                                           _("Join selected endnodes"),
1281                                           INKSCAPE_ICON_NODE_JOIN,
1282                                           secondarySize );
1283         g_object_set( inky, "short_label", _("Join"), NULL );
1284         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1285         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1286     }
1288     {
1289         InkAction* inky = ink_action_new( "NodeBreakAction",
1290                                           _("Break nodes"),
1291                                           _("Break path at selected nodes"),
1292                                           INKSCAPE_ICON_NODE_BREAK,
1293                                           secondarySize );
1294         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1295         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1296     }
1299     {
1300         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1301                                           _("Join with segment"),
1302                                           _("Join selected endnodes with a new segment"),
1303                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1304                                           secondarySize );
1305         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1306         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1307     }
1309     {
1310         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1311                                           _("Delete segment"),
1312                                           _("Delete segment between two non-endpoint nodes"),
1313                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1314                                           secondarySize );
1315         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1316         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1317     }
1319     {
1320         InkAction* inky = ink_action_new( "NodeCuspAction",
1321                                           _("Node Cusp"),
1322                                           _("Make selected nodes corner"),
1323                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1324                                           secondarySize );
1325         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1326         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1327     }
1329     {
1330         InkAction* inky = ink_action_new( "NodeSmoothAction",
1331                                           _("Node Smooth"),
1332                                           _("Make selected nodes smooth"),
1333                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1334                                           secondarySize );
1335         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1336         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1337     }
1339     {
1340         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1341                                           _("Node Symmetric"),
1342                                           _("Make selected nodes symmetric"),
1343                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1344                                           secondarySize );
1345         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1346         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1347     }
1349     {
1350         InkAction* inky = ink_action_new( "NodeAutoAction",
1351                                           _("Node Auto"),
1352                                           _("Make selected nodes auto-smooth"),
1353                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1354                                           secondarySize );
1355         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1356         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1357     }
1359     {
1360         InkAction* inky = ink_action_new( "NodeLineAction",
1361                                           _("Node Line"),
1362                                           _("Make selected segments lines"),
1363                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1364                                           secondarySize );
1365         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1366         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1367     }
1369     {
1370         InkAction* inky = ink_action_new( "NodeCurveAction",
1371                                           _("Node Curve"),
1372                                           _("Make selected segments curves"),
1373                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1374                                           secondarySize );
1375         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1376         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1377     }
1379     {
1380         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1381                                                       _("Show Handles"),
1382                                                       _("Show the Bezier handles of selected nodes"),
1383                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1384                                                       Inkscape::ICON_SIZE_DECORATION );
1385         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1386         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1387         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1388     }
1390     {
1391         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1392                                                       _("Show Outline"),
1393                                                       _("Show the outline of the path"),
1394                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1395                                                       Inkscape::ICON_SIZE_DECORATION );
1396         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1397         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1398         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1399     }
1401     {
1402         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1403                                           _("Next path effect parameter"),
1404                                           _("Show next path effect parameter for editing"),
1405                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1406                                           Inkscape::ICON_SIZE_DECORATION );
1407         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1408         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1409         g_object_set_data( holder, "nodes_lpeedit", inky);
1410     }
1412     {
1413         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1414                                           _("Edit clipping path"),
1415                                           _("Edit the clipping path of the object"),
1416                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1417                                           Inkscape::ICON_SIZE_DECORATION );
1418         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1419         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1420         g_object_set_data( holder, "nodes_clippathedit", inky);
1421     }
1423     {
1424         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1425                                           _("Edit mask path"),
1426                                           _("Edit the mask of the object"),
1427                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1428                                           Inkscape::ICON_SIZE_DECORATION );
1429         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1430         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1431         g_object_set_data( holder, "nodes_maskedit", inky);
1432     }
1434     /* X coord of selected node(s) */
1435     {
1436         EgeAdjustmentAction* eact = 0;
1437         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1438         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1439         eact = create_adjustment_action( "NodeXAction",
1440                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1441                                          "/tools/nodes/Xcoord", 0,
1442                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1443                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1444                                          labels, values, G_N_ELEMENTS(labels),
1445                                          sp_node_path_x_value_changed );
1446         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1447         g_object_set_data( holder, "nodes_x_action", eact );
1448         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1449         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1450     }
1452     /* Y coord of selected node(s) */
1453     {
1454         EgeAdjustmentAction* eact = 0;
1455         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1456         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1457         eact = create_adjustment_action( "NodeYAction",
1458                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1459                                          "/tools/nodes/Ycoord", 0,
1460                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1461                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1462                                          labels, values, G_N_ELEMENTS(labels),
1463                                          sp_node_path_y_value_changed );
1464         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1465         g_object_set_data( holder, "nodes_y_action", eact );
1466         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1467         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1468     }
1470     // add the units menu
1471     {
1472         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1473         gtk_action_group_add_action( mainActions, act );
1474     }
1477     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1479     //watch selection
1480     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1482     sigc::connection *c_selection_changed =
1483         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1484                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1485     pool->add_connection ("selection-changed", c_selection_changed);
1487     sigc::connection *c_selection_modified =
1488         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1489                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1490     pool->add_connection ("selection-modified", c_selection_modified);
1492     sigc::connection *c_subselection_changed =
1493         new sigc::connection (desktop->connectToolSubselectionChanged
1494                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1495     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1497     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1499     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1500 } // end of sp_node_toolbox_prep()
1503 //########################
1504 //##    Zoom Toolbox    ##
1505 //########################
1507 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1509     // no custom GtkAction setup needed
1510 } // end of sp_zoom_toolbox_prep()
1512 void
1513 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1515     toolbox_set_desktop(toolbox,
1516                         desktop,
1517                         setup_tool_toolbox,
1518                         update_tool_toolbox);
1522 void
1523 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1525     toolbox_set_desktop(toolbox,
1526                         desktop,
1527                         setup_aux_toolbox,
1528                         update_aux_toolbox);
1531 void
1532 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1534     toolbox_set_desktop(toolbox,
1535                         desktop,
1536                         setup_commands_toolbox,
1537                         update_commands_toolbox);
1540 void
1541 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1543     toolbox_set_desktop(toolbox,
1544                         desktop,
1545                         setup_snap_toolbox,
1546                         update_snap_toolbox);
1550 static void
1551 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func)
1553     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1554                                                                               "event_context_connection"));
1555     {
1556         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1557         if (g_object_get_data(G_OBJECT(child), "MarkForChild")) {
1558             toolbox = child;
1559         }
1560     }
1561     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1562     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1564     if (old_desktop) {
1565         GList *children, *iter;
1567         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1568         for ( iter = children ; iter ; iter = iter->next ) {
1569             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1570         }
1571         g_list_free(children);
1572     }
1574     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1576     if (desktop) {
1577         gtk_widget_set_sensitive(toolbox, TRUE);
1578         setup_func(toolbox, desktop);
1579         update_func(desktop, desktop->event_context, toolbox);
1580         *conn = desktop->connectEventContextChanged
1581             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1582     } else {
1583         gtk_widget_set_sensitive(toolbox, FALSE);
1584     }
1586 } // end of toolbox_set_desktop()
1589 static void setupToolboxCommon( GtkWidget *toolbox,
1590                                 SPDesktop *desktop,
1591                                 gchar const *descr,
1592                                 gchar const* toolbarName,
1593                                 gchar const* sizePref,
1594                                 GtkOrientation orientation )
1596     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1597     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1599     GtkUIManager* mgr = gtk_ui_manager_new();
1600     GError* errVal = 0;
1602     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1603     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1605     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1606     if ( prefs->getBool("/toolbox/icononly", true) ) {
1607         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1608     }
1610     Inkscape::IconSize toolboxSize = prefToSize(sizePref);
1611     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1613     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1614     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1616     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1618     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1619     if ( child ) {
1620         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1621     }
1623     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1626 static void
1627 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1629     gchar const * descr =
1630         "<ui>"
1631         "  <toolbar name='ToolToolbar'>"
1632         "    <toolitem action='ToolSelector' />"
1633         "    <toolitem action='ToolNode' />"
1634         "    <toolitem action='ToolTweak' />"
1635         "    <toolitem action='ToolSpray' />"
1636         "    <toolitem action='ToolZoom' />"
1637         "    <toolitem action='ToolRect' />"
1638         "    <toolitem action='Tool3DBox' />"
1639         "    <toolitem action='ToolArc' />"
1640         "    <toolitem action='ToolStar' />"
1641         "    <toolitem action='ToolSpiral' />"
1642         "    <toolitem action='ToolPencil' />"
1643         "    <toolitem action='ToolPen' />"
1644         "    <toolitem action='ToolCalligraphic' />"
1645         "    <toolitem action='ToolEraser' />"
1646 //        "    <toolitem action='ToolLPETool' />"
1647         "    <toolitem action='ToolPaintBucket' />"
1648         "    <toolitem action='ToolText' />"
1649         "    <toolitem action='ToolConnector' />"
1650         "    <toolitem action='ToolGradient' />"
1651         "    <toolitem action='ToolDropper' />"
1652         "  </toolbar>"
1653         "</ui>";
1655     setupToolboxCommon( toolbox, desktop, descr,
1656                         "/ui/ToolToolbar",
1657                         "/toolbox/tools/small",
1658                         GTK_ORIENTATION_VERTICAL );
1661 static void
1662 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1664     gchar const *const tname = ( eventcontext
1665                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1666                                  : NULL );
1667     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1669     for (int i = 0 ; tools[i].type_name ; i++ ) {
1670         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1671         if ( act ) {
1672             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1673             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1674             if ( verbAct ) {
1675                 verbAct->set_active(setActive);
1676             }
1677         }
1678     }
1681 static void
1682 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1684     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1685     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1686     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1687     GtkUIManager* mgr = gtk_ui_manager_new();
1688     GError* errVal = 0;
1689     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1690     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1692     std::map<std::string, GtkWidget*> dataHolders;
1694     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1695         if ( aux_toolboxes[i].prep_func ) {
1696             // converted to GtkActions and UIManager
1698             GtkWidget* kludge = gtk_toolbar_new();
1699             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1700             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1701             dataHolders[aux_toolboxes[i].type_name] = kludge;
1702             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1703         } else {
1705             GtkWidget *sub_toolbox = 0;
1706             if (aux_toolboxes[i].create_func == NULL) {
1707                 sub_toolbox = sp_empty_toolbox_new(desktop);
1708             } else {
1709                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1710             }
1712             gtk_size_group_add_widget( grouper, sub_toolbox );
1714             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1715             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1717         }
1718     }
1720     // Second pass to create toolbars *after* all GtkActions are created
1721     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1722         if ( aux_toolboxes[i].prep_func ) {
1723             // converted to GtkActions and UIManager
1725             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1727             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1728             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1730             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1731             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1732             g_free( tmp );
1733             tmp = 0;
1735             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1736                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1737             }
1739             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1740             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1742             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1744             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1745                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1746                 swatch->setDesktop( desktop );
1747                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1748                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1749                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1750                 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 );
1751             }
1753             gtk_widget_show_all( holder );
1754             sp_set_font_size_smaller( holder );
1756             gtk_size_group_add_widget( grouper, holder );
1758             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1759             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1760         }
1761     }
1763     g_object_unref( G_OBJECT(grouper) );
1766 static void
1767 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1769     gchar const *tname = ( eventcontext
1770                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1771                            : NULL );
1772     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1773         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1774         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1775             gtk_widget_show_all(sub_toolbox);
1776             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1777         } else {
1778             gtk_widget_hide(sub_toolbox);
1779         }
1780     }
1783 static void
1784 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1786     gchar const * descr =
1787         "<ui>"
1788         "  <toolbar name='CommandsToolbar'>"
1789         "    <toolitem action='FileNew' />"
1790         "    <toolitem action='FileOpen' />"
1791         "    <toolitem action='FileSave' />"
1792         "    <toolitem action='FilePrint' />"
1793         "    <separator />"
1794         "    <toolitem action='FileImport' />"
1795         "    <toolitem action='FileExport' />"
1796         "    <separator />"
1797         "    <toolitem action='EditUndo' />"
1798         "    <toolitem action='EditRedo' />"
1799         "    <separator />"
1800         "    <toolitem action='EditCopy' />"
1801         "    <toolitem action='EditCut' />"
1802         "    <toolitem action='EditPaste' />"
1803         "    <separator />"
1804         "    <toolitem action='ZoomSelection' />"
1805         "    <toolitem action='ZoomDrawing' />"
1806         "    <toolitem action='ZoomPage' />"
1807         "    <separator />"
1808         "    <toolitem action='EditDuplicate' />"
1809         "    <toolitem action='EditClone' />"
1810         "    <toolitem action='EditUnlinkClone' />"
1811         "    <separator />"
1812         "    <toolitem action='SelectionGroup' />"
1813         "    <toolitem action='SelectionUnGroup' />"
1814         "    <separator />"
1815         "    <toolitem action='DialogFillStroke' />"
1816         "    <toolitem action='DialogText' />"
1817         "    <toolitem action='DialogLayers' />"
1818         "    <toolitem action='DialogXMLEditor' />"
1819         "    <toolitem action='DialogAlignDistribute' />"
1820         "    <separator />"
1821         "    <toolitem action='DialogPreferences' />"
1822         "    <toolitem action='DialogDocumentProperties' />"
1823         "  </toolbar>"
1824         "</ui>";
1826     setupToolboxCommon( toolbox, desktop, descr,
1827                         "/ui/CommandsToolbar",
1828                         "/toolbox/small",
1829                         GTK_ORIENTATION_HORIZONTAL );
1832 static void
1833 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1837 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1839     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1840         return;
1841     }
1843     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1844     g_assert(ptr != NULL);
1846     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1847     SPNamedView *nv = sp_desktop_namedview(dt);
1848     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1850     if (dt == NULL || nv == NULL) {
1851         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1852         return;
1853     }
1855     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1857     if (repr == NULL) {
1858         g_warning("This namedview doesn't have a xml representation attached!");
1859         return;
1860     }
1862     bool saved = sp_document_get_undo_sensitive(doc);
1863     sp_document_set_undo_sensitive(doc, false);
1865     bool v = false;
1866     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1868     switch (attr) {
1869         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1870             dt->toggleSnapGlobal();
1871             break;
1872         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1873             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1874             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1875             break;
1876         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1877             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1878             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1879             break;
1880         case SP_ATTR_INKSCAPE_BBOX_NODES:
1881             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1882             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1883             break;
1884         case SP_ATTR_INKSCAPE_SNAP_NODES:
1885             v = nv->snap_manager.snapprefs.getSnapModeNode();
1886             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1887             break;
1888         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1889             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1890             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1891             break;
1892         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1893             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1894             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1895             break;
1896         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1897             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1898             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1899             break;
1900         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1901             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1902             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1903             break;
1904         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1905             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1906             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1907             break;
1908         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1909             v = nv->snap_manager.snapprefs.getSnapToGrids();
1910             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1911             break;
1912         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1913             v = nv->snap_manager.snapprefs.getSnapToGuides();
1914             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1915             break;
1916         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1917             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1918             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1919             break;
1920             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1921               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1922               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1923               break;*/
1924         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1925             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1926             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1927             break;
1928         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1929             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1930             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1931             break;
1932         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1933             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1934             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1935             break;
1936         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1937             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1938             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1939             break;
1940         default:
1941             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1942             break;
1943     }
1945     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1946     doc->setModifiedSinceSave();
1948     sp_document_set_undo_sensitive(doc, saved);
1951 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1953     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1955     gchar const * descr =
1956         "<ui>"
1957         "  <toolbar name='SnapToolbar'>"
1958         "    <toolitem action='ToggleSnapGlobal' />"
1959         "    <separator />"
1960         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
1961         "    <toolitem action='ToggleSnapToBBoxPath' />"
1962         "    <toolitem action='ToggleSnapToBBoxNode' />"
1963         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1964         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
1965         "    <separator />"
1966         "    <toolitem action='ToggleSnapFromNode' />"
1967         "    <toolitem action='ToggleSnapToItemPath' />"
1968         "    <toolitem action='ToggleSnapToPathIntersections' />"
1969         "    <toolitem action='ToggleSnapToItemNode' />"
1970         "    <toolitem action='ToggleSnapToSmoothNodes' />"
1971         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
1972         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
1973         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
1974         "    <separator />"
1975         "    <toolitem action='ToggleSnapToPageBorder' />"
1976         "    <toolitem action='ToggleSnapToGrids' />"
1977         "    <toolitem action='ToggleSnapToGuides' />"
1978         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
1979         "  </toolbar>"
1980         "</ui>";
1982     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1984     {
1985         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
1986                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
1987                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
1989         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1990         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
1991     }
1993     {
1994         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
1995                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
1996                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
1998         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1999         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2000     }
2002     {
2003         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2004                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2005                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2007         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2008         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2009     }
2011     {
2012         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2013                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2014                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2016         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2017         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2018     }
2020     {
2021         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2022                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2023                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2024                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2026         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2027         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2028     }
2030     {
2031         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2032                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2033                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2035         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2036         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2037     }
2039     {
2040         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2041                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2043         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2044         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2045     }
2047     {
2048         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2049                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2050                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2052         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2053         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2054     }
2056     {
2057         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2058                                                      _("Path intersections"), _("Snap to path intersections"),
2059                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2061         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2062         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2063     }
2065     {
2066         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2067                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2068                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2070         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2071         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2072     }
2074     {
2075         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2076                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2077                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2079         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2080         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2081     }
2083     {
2084         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2085                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2086                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2088         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2089         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2090     }
2092     {
2093         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2094                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2095                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2097         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2098         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2099     }
2101     {
2102         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2103                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2104                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2106         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2107         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2108     }
2110     {
2111         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2112                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2113                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2115         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2116         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2117     }
2119     {
2120         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2121                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2122                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2124         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2125         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2126     }
2128     {
2129         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2130                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2131                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2133         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2134         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2135     }
2137     /*{
2138       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2139       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2140       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2141       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2143       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2144       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2145       }*/
2147     setupToolboxCommon( toolbox, desktop, descr,
2148                         "/ui/SnapToolbar",
2149                         "/toolbox/secondary",
2150                         GTK_ORIENTATION_HORIZONTAL );
2153 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2155     g_assert(desktop != NULL);
2156     g_assert(toolbox != NULL);
2158     SPNamedView *nv = sp_desktop_namedview(desktop);
2159     if (nv == NULL) {
2160         g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2161         return;
2162     }
2164     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2166     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2167     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2168     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2169     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2170     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2171     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2172     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2173     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2174     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2175     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2176     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2177     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2178     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2179     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2180     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2181     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2182     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2183     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2186     if (!act1) {
2187         return; // The snap actions haven't been defined yet (might be the case during startup)
2188     }
2190     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2191     // changes in our document because we're only updating the UI;
2192     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2193     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2195     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2196     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2198     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2199     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2200     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2202     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2203     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2204     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2205     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2206     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2207     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2208     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2209     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2211     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2212     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2213     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2215     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2216     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2217     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2218     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2219     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2220     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2221     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2222     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2223     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2224     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2225     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2226     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2227     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2228     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2229     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2231     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2232     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2233     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2234     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2236     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2237     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2238     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2239     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2242     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2245 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2247     gtk_widget_show(toolbox_toplevel);
2248     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2250     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2251     if (!shown_toolbox) {
2252         return;
2253     }
2254     gtk_widget_show(toolbox);
2256     gtk_widget_show_all(shown_toolbox);
2259 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2261     GtkWidget *tbl = gtk_toolbar_new();
2262     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2263     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2265     gtk_widget_show_all(tbl);
2266     sp_set_font_size_smaller (tbl);
2268     return tbl;
2271 #define MODE_LABEL_WIDTH 70
2273 //########################
2274 //##       Star         ##
2275 //########################
2277 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2279     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2281     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2282         // do not remember prefs if this call is initiated by an undo change, because undoing object
2283         // creation sets bogus values to its attributes before it is deleted
2284         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2285         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2286     }
2288     // quit if run by the attr_changed listener
2289     if (g_object_get_data( dataKludge, "freeze" )) {
2290         return;
2291     }
2293     // in turn, prevent listener from responding
2294     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2296     bool modmade = false;
2298     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2299     GSList const *items = selection->itemList();
2300     for (; items != NULL; items = items->next) {
2301         if (SP_IS_STAR((SPItem *) items->data)) {
2302             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2303             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2304             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2305                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2306                                     + M_PI / (gint)adj->value));
2307             SP_OBJECT((SPItem *) items->data)->updateRepr();
2308             modmade = true;
2309         }
2310     }
2311     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2312                                    _("Star: Change number of corners"));
2314     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2317 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2319     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2321     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2322         if (!IS_NAN(adj->value)) {
2323             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2324             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2325         }
2326     }
2328     // quit if run by the attr_changed listener
2329     if (g_object_get_data( dataKludge, "freeze" )) {
2330         return;
2331     }
2333     // in turn, prevent listener from responding
2334     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2336     bool modmade = false;
2337     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2338     GSList const *items = selection->itemList();
2339     for (; items != NULL; items = items->next) {
2340         if (SP_IS_STAR((SPItem *) items->data)) {
2341             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2343             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2344             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2345             if (r2 < r1) {
2346                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2347             } else {
2348                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2349             }
2351             SP_OBJECT((SPItem *) items->data)->updateRepr();
2352             modmade = true;
2353         }
2354     }
2356     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2357                                    _("Star: Change spoke ratio"));
2359     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2362 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2364     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2365     bool flat = ege_select_one_action_get_active( act ) == 0;
2367     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2368         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2369         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2370     }
2372     // quit if run by the attr_changed listener
2373     if (g_object_get_data( dataKludge, "freeze" )) {
2374         return;
2375     }
2377     // in turn, prevent listener from responding
2378     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2380     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2381     GSList const *items = selection->itemList();
2382     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2383     bool modmade = false;
2385     if ( prop_action ) {
2386         gtk_action_set_sensitive( prop_action, !flat );
2387     }
2389     for (; items != NULL; items = items->next) {
2390         if (SP_IS_STAR((SPItem *) items->data)) {
2391             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2392             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2393             SP_OBJECT((SPItem *) items->data)->updateRepr();
2394             modmade = true;
2395         }
2396     }
2398     if (modmade) {
2399         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2400                          flat ? _("Make polygon") : _("Make star"));
2401     }
2403     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2406 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2408     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2410     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2411         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2412         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2413     }
2415     // quit if run by the attr_changed listener
2416     if (g_object_get_data( dataKludge, "freeze" )) {
2417         return;
2418     }
2420     // in turn, prevent listener from responding
2421     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2423     bool modmade = false;
2425     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2426     GSList const *items = selection->itemList();
2427     for (; items != NULL; items = items->next) {
2428         if (SP_IS_STAR((SPItem *) items->data)) {
2429             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2430             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2431             SP_OBJECT(items->data)->updateRepr();
2432             modmade = true;
2433         }
2434     }
2435     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2436                                    _("Star: Change rounding"));
2438     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2441 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2443     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2445     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2446         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2447         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2448     }
2450     // quit if run by the attr_changed listener
2451     if (g_object_get_data( dataKludge, "freeze" )) {
2452         return;
2453     }
2455     // in turn, prevent listener from responding
2456     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2458     bool modmade = false;
2460     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2461     GSList const *items = selection->itemList();
2462     for (; items != NULL; items = items->next) {
2463         if (SP_IS_STAR((SPItem *) items->data)) {
2464             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2465             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2466             SP_OBJECT(items->data)->updateRepr();
2467             modmade = true;
2468         }
2469     }
2470     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2471                                    _("Star: Change randomization"));
2473     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2477 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2478                                        gchar const */*old_value*/, gchar const */*new_value*/,
2479                                        bool /*is_interactive*/, gpointer data)
2481     GtkWidget *tbl = GTK_WIDGET(data);
2483     // quit if run by the _changed callbacks
2484     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2485         return;
2486     }
2488     // in turn, prevent callbacks from responding
2489     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2491     GtkAdjustment *adj = 0;
2493     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2494     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2496     if (!strcmp(name, "inkscape:randomized")) {
2497         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2498         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2499     } else if (!strcmp(name, "inkscape:rounded")) {
2500         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2501         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2502     } else if (!strcmp(name, "inkscape:flatsided")) {
2503         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2504         char const *flatsides = repr->attribute("inkscape:flatsided");
2505         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2506         if ( flatsides && !strcmp(flatsides,"false") ) {
2507             ege_select_one_action_set_active( flat_action, 1 );
2508             gtk_action_set_sensitive( prop_action, TRUE );
2509         } else {
2510             ege_select_one_action_set_active( flat_action, 0 );
2511             gtk_action_set_sensitive( prop_action, FALSE );
2512         }
2513     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2514         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2515         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2516         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2517         if (r2 < r1) {
2518             gtk_adjustment_set_value(adj, r2/r1);
2519         } else {
2520             gtk_adjustment_set_value(adj, r1/r2);
2521         }
2522     } else if (!strcmp(name, "sodipodi:sides")) {
2523         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2524         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2525     }
2527     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2531 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2533     NULL, /* child_added */
2534     NULL, /* child_removed */
2535     star_tb_event_attr_changed,
2536     NULL, /* content_changed */
2537     NULL  /* order_changed */
2538 };
2541 /**
2542  *  \param selection Should not be NULL.
2543  */
2544 static void
2545 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2547     int n_selected = 0;
2548     Inkscape::XML::Node *repr = NULL;
2550     purge_repr_listener( tbl, tbl );
2552     for (GSList const *items = selection->itemList();
2553          items != NULL;
2554          items = items->next)
2555     {
2556         if (SP_IS_STAR((SPItem *) items->data)) {
2557             n_selected++;
2558             repr = SP_OBJECT_REPR((SPItem *) items->data);
2559         }
2560     }
2562     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2564     if (n_selected == 0) {
2565         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2566     } else if (n_selected == 1) {
2567         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2569         if (repr) {
2570             g_object_set_data( tbl, "repr", repr );
2571             Inkscape::GC::anchor(repr);
2572             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2573             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2574         }
2575     } else {
2576         // FIXME: implement averaging of all parameters for multiple selected stars
2577         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2578         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2579     }
2583 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2585     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2586     // callbacks to lump all the changes for all selected objects in one undo step
2588     GtkAdjustment *adj = 0;
2590     // fixme: make settable in prefs!
2591     gint mag = 5;
2592     gdouble prop = 0.5;
2593     gboolean flat = FALSE;
2594     gdouble randomized = 0;
2595     gdouble rounded = 0;
2597     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2598     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2600     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2601     gtk_action_set_sensitive( sb2, !flat );
2603     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2604     gtk_adjustment_set_value(adj, mag);
2605     gtk_adjustment_value_changed(adj);
2607     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2608     gtk_adjustment_set_value(adj, prop);
2609     gtk_adjustment_value_changed(adj);
2611     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2612     gtk_adjustment_set_value(adj, rounded);
2613     gtk_adjustment_value_changed(adj);
2615     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2616     gtk_adjustment_set_value(adj, randomized);
2617     gtk_adjustment_value_changed(adj);
2621 void
2622 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2624     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2625     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2626     GtkWidget *l = gtk_label_new(NULL);
2627     gtk_label_set_markup(GTK_LABEL(l), title);
2628     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2629     if ( GTK_IS_TOOLBAR(tbl) ) {
2630         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2631     } else {
2632         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2633     }
2634     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2638 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2640     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2642     {
2643         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2644         ege_output_action_set_use_markup( act, TRUE );
2645         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2646         g_object_set_data( holder, "mode_action", act );
2647     }
2649     {
2650         EgeAdjustmentAction* eact = 0;
2651         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2652         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2654         /* Flatsided checkbox */
2655         {
2656             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2658             GtkTreeIter iter;
2659             gtk_list_store_append( model, &iter );
2660             gtk_list_store_set( model, &iter,
2661                                 0, _("Polygon"),
2662                                 1, _("Regular polygon (with one handle) instead of a star"),
2663                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2664                                 -1 );
2666             gtk_list_store_append( model, &iter );
2667             gtk_list_store_set( model, &iter,
2668                                 0, _("Star"),
2669                                 1, _("Star instead of a regular polygon (with one handle)"),
2670                                 2, INKSCAPE_ICON_DRAW_STAR,
2671                                 -1 );
2673             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2674             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2675             g_object_set_data( holder, "flat_action", act );
2677             ege_select_one_action_set_appearance( act, "full" );
2678             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2679             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2680             ege_select_one_action_set_icon_column( act, 2 );
2681             ege_select_one_action_set_icon_size( act, secondarySize );
2682             ege_select_one_action_set_tooltip_column( act, 1  );
2684             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2685             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2686         }
2688         /* Magnitude */
2689         {
2690         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2691         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2692         eact = create_adjustment_action( "MagnitudeAction",
2693                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2694                                          "/tools/shapes/star/magnitude", 3,
2695                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2696                                          3, 1024, 1, 5,
2697                                          labels, values, G_N_ELEMENTS(labels),
2698                                          sp_stb_magnitude_value_changed,
2699                                          1.0, 0 );
2700         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2701         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2702         }
2704         /* Spoke ratio */
2705         {
2706         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2707         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2708         eact = create_adjustment_action( "SpokeAction",
2709                                          _("Spoke ratio"), _("Spoke ratio:"),
2710                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2711                                          // Base radius is the same for the closest handle.
2712                                          _("Base radius to tip radius ratio"),
2713                                          "/tools/shapes/star/proportion", 0.5,
2714                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2715                                          0.01, 1.0, 0.01, 0.1,
2716                                          labels, values, G_N_ELEMENTS(labels),
2717                                          sp_stb_proportion_value_changed );
2718         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2719         g_object_set_data( holder, "prop_action", eact );
2720         }
2722         if ( !isFlatSided ) {
2723             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2724         } else {
2725             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2726         }
2728         /* Roundedness */
2729         {
2730         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2731         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2732         eact = create_adjustment_action( "RoundednessAction",
2733                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2734                                          "/tools/shapes/star/rounded", 0.0,
2735                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2736                                          -10.0, 10.0, 0.01, 0.1,
2737                                          labels, values, G_N_ELEMENTS(labels),
2738                                          sp_stb_rounded_value_changed );
2739         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2740         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2741         }
2743         /* Randomization */
2744         {
2745         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2746         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2747         eact = create_adjustment_action( "RandomizationAction",
2748                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2749                                          "/tools/shapes/star/randomized", 0.0,
2750                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2751                                          -10.0, 10.0, 0.001, 0.01,
2752                                          labels, values, G_N_ELEMENTS(labels),
2753                                          sp_stb_randomized_value_changed, 0.1, 3 );
2754         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2755         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2756         }
2757     }
2759     {
2760         /* Reset */
2761         {
2762             GtkAction* act = gtk_action_new( "StarResetAction",
2763                                              _("Defaults"),
2764                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2765                                              GTK_STOCK_CLEAR );
2766             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2767             gtk_action_group_add_action( mainActions, act );
2768             gtk_action_set_sensitive( act, TRUE );
2769         }
2770     }
2772     sigc::connection *connection = new sigc::connection(
2773         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2774         );
2775     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2776     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2780 //########################
2781 //##       Rect         ##
2782 //########################
2784 static void sp_rtb_sensitivize( GObject *tbl )
2786     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2787     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2788     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2790     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2791         gtk_action_set_sensitive( not_rounded, FALSE );
2792     } else {
2793         gtk_action_set_sensitive( not_rounded, TRUE );
2794     }
2798 static void
2799 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2800                           void (*setter)(SPRect *, gdouble))
2802     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2804     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2805     SPUnit const *unit = tracker->getActiveUnit();
2807     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2808         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2809         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2810     }
2812     // quit if run by the attr_changed listener
2813     if (g_object_get_data( tbl, "freeze" )) {
2814         return;
2815     }
2817     // in turn, prevent listener from responding
2818     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2820     bool modmade = false;
2821     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2822     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2823         if (SP_IS_RECT(items->data)) {
2824             if (adj->value != 0) {
2825                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2826             } else {
2827                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2828             }
2829             modmade = true;
2830         }
2831     }
2833     sp_rtb_sensitivize( tbl );
2835     if (modmade) {
2836         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2837                                    _("Change rectangle"));
2838     }
2840     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2843 static void
2844 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2846     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2849 static void
2850 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2852     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2855 static void
2856 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2858     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2861 static void
2862 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2864     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2869 static void
2870 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2872     GtkAdjustment *adj = 0;
2874     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2875     gtk_adjustment_set_value(adj, 0.0);
2876     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2877     gtk_adjustment_value_changed(adj);
2879     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2880     gtk_adjustment_set_value(adj, 0.0);
2881     gtk_adjustment_value_changed(adj);
2883     sp_rtb_sensitivize( obj );
2886 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2887                                        gchar const */*old_value*/, gchar const */*new_value*/,
2888                                        bool /*is_interactive*/, gpointer data)
2890     GObject *tbl = G_OBJECT(data);
2892     // quit if run by the _changed callbacks
2893     if (g_object_get_data( tbl, "freeze" )) {
2894         return;
2895     }
2897     // in turn, prevent callbacks from responding
2898     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2900     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2901     SPUnit const *unit = tracker->getActiveUnit();
2903     gpointer item = g_object_get_data( tbl, "item" );
2904     if (item && SP_IS_RECT(item)) {
2905         {
2906             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2907             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2908             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2909         }
2911         {
2912             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2913             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2914             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2915         }
2917         {
2918             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2919             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2920             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2921         }
2923         {
2924             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2925             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2926             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2927         }
2928     }
2930     sp_rtb_sensitivize( tbl );
2932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2936 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2937     NULL, /* child_added */
2938     NULL, /* child_removed */
2939     rect_tb_event_attr_changed,
2940     NULL, /* content_changed */
2941     NULL  /* order_changed */
2942 };
2944 /**
2945  *  \param selection should not be NULL.
2946  */
2947 static void
2948 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2950     int n_selected = 0;
2951     Inkscape::XML::Node *repr = NULL;
2952     SPItem *item = NULL;
2954     if ( g_object_get_data( tbl, "repr" ) ) {
2955         g_object_set_data( tbl, "item", NULL );
2956     }
2957     purge_repr_listener( tbl, tbl );
2959     for (GSList const *items = selection->itemList();
2960          items != NULL;
2961          items = items->next) {
2962         if (SP_IS_RECT((SPItem *) items->data)) {
2963             n_selected++;
2964             item = (SPItem *) items->data;
2965             repr = SP_OBJECT_REPR(item);
2966         }
2967     }
2969     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2971     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2973     if (n_selected == 0) {
2974         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2976         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2977         gtk_action_set_sensitive(w, FALSE);
2978         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2979         gtk_action_set_sensitive(h, FALSE);
2981     } else if (n_selected == 1) {
2982         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2983         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2985         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2986         gtk_action_set_sensitive(w, TRUE);
2987         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2988         gtk_action_set_sensitive(h, TRUE);
2990         if (repr) {
2991             g_object_set_data( tbl, "repr", repr );
2992             g_object_set_data( tbl, "item", item );
2993             Inkscape::GC::anchor(repr);
2994             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2995             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2996         }
2997     } else {
2998         // FIXME: implement averaging of all parameters for multiple selected
2999         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3000         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3001         sp_rtb_sensitivize( tbl );
3002     }
3006 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3008     EgeAdjustmentAction* eact = 0;
3009     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3011     {
3012         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3013         ege_output_action_set_use_markup( act, TRUE );
3014         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3015         g_object_set_data( holder, "mode_action", act );
3016     }
3018     // rx/ry units menu: create
3019     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3020     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3021     // fixme: add % meaning per cent of the width/height
3022     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3023     g_object_set_data( holder, "tracker", tracker );
3025     /* W */
3026     {
3027         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3028         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3029         eact = create_adjustment_action( "RectWidthAction",
3030                                          _("Width"), _("W:"), _("Width of rectangle"),
3031                                          "/tools/shapes/rect/width", 0,
3032                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3033                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3034                                          labels, values, G_N_ELEMENTS(labels),
3035                                          sp_rtb_width_value_changed );
3036         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3037         g_object_set_data( holder, "width_action", eact );
3038         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3039         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3040     }
3042     /* H */
3043     {
3044         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3045         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3046         eact = create_adjustment_action( "RectHeightAction",
3047                                          _("Height"), _("H:"), _("Height of rectangle"),
3048                                          "/tools/shapes/rect/height", 0,
3049                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3050                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3051                                          labels, values, G_N_ELEMENTS(labels),
3052                                          sp_rtb_height_value_changed );
3053         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3054         g_object_set_data( holder, "height_action", eact );
3055         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3056         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3057     }
3059     /* rx */
3060     {
3061         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3062         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3063         eact = create_adjustment_action( "RadiusXAction",
3064                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3065                                          "/tools/shapes/rect/rx", 0,
3066                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3067                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3068                                          labels, values, G_N_ELEMENTS(labels),
3069                                          sp_rtb_rx_value_changed);
3070         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3071         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3072     }
3074     /* ry */
3075     {
3076         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3077         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3078         eact = create_adjustment_action( "RadiusYAction",
3079                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3080                                          "/tools/shapes/rect/ry", 0,
3081                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3082                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3083                                          labels, values, G_N_ELEMENTS(labels),
3084                                          sp_rtb_ry_value_changed);
3085         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3086         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3087     }
3089     // add the units menu
3090     {
3091         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3092         gtk_action_group_add_action( mainActions, act );
3093     }
3095     /* Reset */
3096     {
3097         InkAction* inky = ink_action_new( "RectResetAction",
3098                                           _("Not rounded"),
3099                                           _("Make corners sharp"),
3100                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3101                                           secondarySize );
3102         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3103         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3104         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3105         g_object_set_data( holder, "not_rounded", inky );
3106     }
3108     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3109     sp_rtb_sensitivize( holder );
3111     sigc::connection *connection = new sigc::connection(
3112         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3113         );
3114     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3115     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3118 //########################
3119 //##       3D Box       ##
3120 //########################
3122 // normalize angle so that it lies in the interval [0,360]
3123 static double box3d_normalize_angle (double a) {
3124     double angle = a + ((int) (a/360.0))*360;
3125     if (angle < 0) {
3126         angle += 360.0;
3127     }
3128     return angle;
3131 static void
3132 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3133                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3134     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3135     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3136     //       are reset).
3137     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3139     if (is_infinite) {
3140         gtk_toggle_action_set_active(tact, TRUE);
3141         gtk_action_set_sensitive(act, TRUE);
3143         double angle = persp3d_get_infinite_angle(persp, axis);
3144         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3145             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3146         }
3147     } else {
3148         gtk_toggle_action_set_active(tact, FALSE);
3149         gtk_action_set_sensitive(act, FALSE);
3150     }
3153 static void
3154 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3155     if (!persp_repr) {
3156         g_print ("No perspective given to box3d_resync_toolbar().\n");
3157         return;
3158     }
3160     GtkWidget *tbl = GTK_WIDGET(data);
3161     GtkAdjustment *adj = 0;
3162     GtkAction *act = 0;
3163     GtkToggleAction *tact = 0;
3164     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3165     if (!persp) {
3166         // Hmm, is it an error if this happens?
3167         return;
3168     }
3169     {
3170         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3171         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3172         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3174         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3175     }
3176     {
3177         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3178         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3179         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3181         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3182     }
3183     {
3184         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3185         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3186         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3188         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3189     }
3192 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3193                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3194                                                   bool /*is_interactive*/, gpointer data)
3196     GtkWidget *tbl = GTK_WIDGET(data);
3198     // quit if run by the attr_changed or selection changed listener
3199     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3200         return;
3201     }
3203     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3204     // sp_document_maybe_done() when the document is undo insensitive)
3205     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3207     // TODO: Only update the appropriate part of the toolbar
3208 //    if (!strcmp(name, "inkscape:vp_z")) {
3209         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3210 //    }
3212     Persp3D *persp = persp3d_get_from_repr(repr);
3213     persp3d_update_box_reprs(persp);
3215     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3218 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3220     NULL, /* child_added */
3221     NULL, /* child_removed */
3222     box3d_persp_tb_event_attr_changed,
3223     NULL, /* content_changed */
3224     NULL  /* order_changed */
3225 };
3227 /**
3228  *  \param selection Should not be NULL.
3229  */
3230 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3231 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3232 static void
3233 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3235     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3236     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3237     // update the perspectives with infinite VPs and leave the other ones untouched).
3239     Inkscape::XML::Node *persp_repr = NULL;
3240     purge_repr_listener(tbl, tbl);
3242     SPItem *item = selection->singleItem();
3243     if (item && SP_IS_BOX3D(item)) {
3244         // FIXME: Also deal with multiple selected boxes
3245         SPBox3D *box = SP_BOX3D(item);
3246         Persp3D *persp = box3d_get_perspective(box);
3247         persp_repr = SP_OBJECT_REPR(persp);
3248         if (persp_repr) {
3249             g_object_set_data(tbl, "repr", persp_repr);
3250             Inkscape::GC::anchor(persp_repr);
3251             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3252             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3253         }
3255         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3256         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3257         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3259         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3260         box3d_resync_toolbar(persp_repr, tbl);
3261         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3262     }
3265 static void
3266 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3268     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3269     SPDocument *document = sp_desktop_document(desktop);
3271     // quit if run by the attr_changed or selection changed listener
3272     if (g_object_get_data( dataKludge, "freeze" )) {
3273         return;
3274     }
3276     // in turn, prevent listener from responding
3277     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3279     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3280     if (sel_persps.empty()) {
3281         // this can happen when the document is created; we silently ignore it
3282         return;
3283     }
3284     Persp3D *persp = sel_persps.front();
3286     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3287     SP_OBJECT(persp)->updateRepr();
3289     // TODO: use the correct axis here, too
3290     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3292     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3296 static void
3297 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3299     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3302 static void
3303 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3305     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3308 static void
3309 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3311     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3315 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3317     // TODO: Take all selected perspectives into account
3318     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3319     if (sel_persps.empty()) {
3320         // this can happen when the document is created; we silently ignore it
3321         return;
3322     }
3323     Persp3D *persp = sel_persps.front();
3325     bool set_infinite = gtk_toggle_action_get_active(act);
3326     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3329 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3331     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3334 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3336     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3339 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3341     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3344 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3346     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3347     EgeAdjustmentAction* eact = 0;
3348     SPDocument *document = sp_desktop_document (desktop);
3349     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3351     EgeAdjustmentAction* box3d_angle_x = 0;
3352     EgeAdjustmentAction* box3d_angle_y = 0;
3353     EgeAdjustmentAction* box3d_angle_z = 0;
3355     /* Angle X */
3356     {
3357         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3358         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3359         eact = create_adjustment_action( "3DBoxAngleXAction",
3360                                          _("Angle in X direction"), _("Angle X:"),
3361                                          // Translators: PL is short for 'perspective line'
3362                                          _("Angle of PLs in X direction"),
3363                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3364                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3365                                          -360.0, 360.0, 1.0, 10.0,
3366                                          labels, values, G_N_ELEMENTS(labels),
3367                                          box3d_angle_x_value_changed );
3368         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3369         g_object_set_data( holder, "box3d_angle_x_action", eact );
3370         box3d_angle_x = eact;
3371     }
3373     if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3374         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3375     } else {
3376         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3377     }
3380     /* VP X state */
3381     {
3382         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3383                                                       // Translators: VP is short for 'vanishing point'
3384                                                       _("State of VP in X direction"),
3385                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3386                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3387                                                       Inkscape::ICON_SIZE_DECORATION );
3388         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3389         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3390         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3391         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3392         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3393     }
3395     /* Angle Y */
3396     {
3397         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3398         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3399         eact = create_adjustment_action( "3DBoxAngleYAction",
3400                                          _("Angle in Y direction"), _("Angle Y:"),
3401                                          // Translators: PL is short for 'perspective line'
3402                                          _("Angle of PLs in Y direction"),
3403                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3404                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3405                                          -360.0, 360.0, 1.0, 10.0,
3406                                          labels, values, G_N_ELEMENTS(labels),
3407                                          box3d_angle_y_value_changed );
3408         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3409         g_object_set_data( holder, "box3d_angle_y_action", eact );
3410         box3d_angle_y = eact;
3411     }
3413     if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3414         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3415     } else {
3416         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3417     }
3419     /* VP Y state */
3420     {
3421         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3422                                                       // Translators: VP is short for 'vanishing point'
3423                                                       _("State of VP in Y direction"),
3424                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3425                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3426                                                       Inkscape::ICON_SIZE_DECORATION );
3427         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3428         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3429         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3430         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3431         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3432     }
3434     /* Angle Z */
3435     {
3436         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3437         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3438         eact = create_adjustment_action( "3DBoxAngleZAction",
3439                                          _("Angle in Z direction"), _("Angle Z:"),
3440                                          // Translators: PL is short for 'perspective line'
3441                                          _("Angle of PLs in Z direction"),
3442                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3443                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3444                                          -360.0, 360.0, 1.0, 10.0,
3445                                          labels, values, G_N_ELEMENTS(labels),
3446                                          box3d_angle_z_value_changed );
3447         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3448         g_object_set_data( holder, "box3d_angle_z_action", eact );
3449         box3d_angle_z = eact;
3450     }
3452     if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3453         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3454     } else {
3455         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3456     }
3458     /* VP Z state */
3459     {
3460         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3461                                                       // Translators: VP is short for 'vanishing point'
3462                                                       _("State of VP in Z direction"),
3463                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3464                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3465                                                       Inkscape::ICON_SIZE_DECORATION );
3466         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3467         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3468         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3469         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3470         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3471     }
3473     sigc::connection *connection = new sigc::connection(
3474         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3475        );
3476     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3477     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3480 //########################
3481 //##       Spiral       ##
3482 //########################
3484 static void
3485 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3487     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3489     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3490         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3491         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3492     }
3494     // quit if run by the attr_changed listener
3495     if (g_object_get_data( tbl, "freeze" )) {
3496         return;
3497     }
3499     // in turn, prevent listener from responding
3500     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3502     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3504     bool modmade = false;
3505     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3506          items != NULL;
3507          items = items->next)
3508     {
3509         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3510             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3511             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3512             SP_OBJECT((SPItem *) items->data)->updateRepr();
3513             modmade = true;
3514         }
3515     }
3517     g_free(namespaced_name);
3519     if (modmade) {
3520         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3521                                    _("Change spiral"));
3522     }
3524     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3527 static void
3528 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3530     sp_spl_tb_value_changed(adj, tbl, "revolution");
3533 static void
3534 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3536     sp_spl_tb_value_changed(adj, tbl, "expansion");
3539 static void
3540 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3542     sp_spl_tb_value_changed(adj, tbl, "t0");
3545 static void
3546 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3548     GtkWidget *tbl = GTK_WIDGET(obj);
3550     GtkAdjustment *adj;
3552     // fixme: make settable
3553     gdouble rev = 5;
3554     gdouble exp = 1.0;
3555     gdouble t0 = 0.0;
3557     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3558     gtk_adjustment_set_value(adj, rev);
3559     gtk_adjustment_value_changed(adj);
3561     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3562     gtk_adjustment_set_value(adj, exp);
3563     gtk_adjustment_value_changed(adj);
3565     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3566     gtk_adjustment_set_value(adj, t0);
3567     gtk_adjustment_value_changed(adj);
3569     spinbutton_defocus(GTK_OBJECT(tbl));
3573 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3574                                          gchar const */*old_value*/, gchar const */*new_value*/,
3575                                          bool /*is_interactive*/, gpointer data)
3577     GtkWidget *tbl = GTK_WIDGET(data);
3579     // quit if run by the _changed callbacks
3580     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3581         return;
3582     }
3584     // in turn, prevent callbacks from responding
3585     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3587     GtkAdjustment *adj;
3588     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3589     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3591     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3592     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3594     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3595     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3597     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3601 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3602     NULL, /* child_added */
3603     NULL, /* child_removed */
3604     spiral_tb_event_attr_changed,
3605     NULL, /* content_changed */
3606     NULL  /* order_changed */
3607 };
3609 static void
3610 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3612     int n_selected = 0;
3613     Inkscape::XML::Node *repr = NULL;
3615     purge_repr_listener( tbl, tbl );
3617     for (GSList const *items = selection->itemList();
3618          items != NULL;
3619          items = items->next)
3620     {
3621         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3622             n_selected++;
3623             repr = SP_OBJECT_REPR((SPItem *) items->data);
3624         }
3625     }
3627     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3629     if (n_selected == 0) {
3630         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3631     } else if (n_selected == 1) {
3632         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3634         if (repr) {
3635             g_object_set_data( tbl, "repr", repr );
3636             Inkscape::GC::anchor(repr);
3637             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3638             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3639         }
3640     } else {
3641         // FIXME: implement averaging of all parameters for multiple selected
3642         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3643         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3644     }
3648 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3650     EgeAdjustmentAction* eact = 0;
3651     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3653     {
3654         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3655         ege_output_action_set_use_markup( act, TRUE );
3656         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3657         g_object_set_data( holder, "mode_action", act );
3658     }
3660     /* Revolution */
3661     {
3662         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3663         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3664         eact = create_adjustment_action( "SpiralRevolutionAction",
3665                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3666                                          "/tools/shapes/spiral/revolution", 3.0,
3667                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3668                                          0.01, 1024.0, 0.1, 1.0,
3669                                          labels, values, G_N_ELEMENTS(labels),
3670                                          sp_spl_tb_revolution_value_changed, 1, 2);
3671         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3672     }
3674     /* Expansion */
3675     {
3676         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3677         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3678         eact = create_adjustment_action( "SpiralExpansionAction",
3679                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3680                                          "/tools/shapes/spiral/expansion", 1.0,
3681                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3682                                          0.0, 1000.0, 0.01, 1.0,
3683                                          labels, values, G_N_ELEMENTS(labels),
3684                                          sp_spl_tb_expansion_value_changed);
3685         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3686     }
3688     /* T0 */
3689     {
3690         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3691         gdouble values[] = {0, 0.5, 0.9};
3692         eact = create_adjustment_action( "SpiralT0Action",
3693                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3694                                          "/tools/shapes/spiral/t0", 0.0,
3695                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3696                                          0.0, 0.999, 0.01, 1.0,
3697                                          labels, values, G_N_ELEMENTS(labels),
3698                                          sp_spl_tb_t0_value_changed);
3699         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3700     }
3702     /* Reset */
3703     {
3704         InkAction* inky = ink_action_new( "SpiralResetAction",
3705                                           _("Defaults"),
3706                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3707                                           GTK_STOCK_CLEAR,
3708                                           secondarySize );
3709         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3710         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3711     }
3714     sigc::connection *connection = new sigc::connection(
3715         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3716         );
3717     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3718     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3721 //########################
3722 //##     Pen/Pencil     ##
3723 //########################
3725 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3726 static Glib::ustring const
3727 freehand_tool_name(GObject *dataKludge)
3729     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3730     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3731              ? "/tools/freehand/pen"
3732              : "/tools/freehand/pencil" );
3735 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3737     gint mode = ege_select_one_action_get_active(act);
3739     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3740     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3742     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3744     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3745     // preparatory work here
3746     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3747         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3748         sp_pen_context_set_polyline_mode(pc);
3749     }
3752 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3754     /* Freehand mode toggle buttons */
3755     {
3756         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3757         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3758         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3760         {
3761             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3763             GtkTreeIter iter;
3764             gtk_list_store_append( model, &iter );
3765             gtk_list_store_set( model, &iter,
3766                                 0, _("Bezier"),
3767                                 1, _("Create regular Bezier path"),
3768                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3769                                 -1 );
3771             gtk_list_store_append( model, &iter );
3772             gtk_list_store_set( model, &iter,
3773                                 0, _("Spiro"),
3774                                 1, _("Create Spiro path"),
3775                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3776                                 -1 );
3778             if (!tool_is_pencil) {
3779                 gtk_list_store_append( model, &iter );
3780                 gtk_list_store_set( model, &iter,
3781                                     0, _("Zigzag"),
3782                                     1, _("Create a sequence of straight line segments"),
3783                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3784                                     -1 );
3786                 gtk_list_store_append( model, &iter );
3787                 gtk_list_store_set( model, &iter,
3788                                     0, _("Paraxial"),
3789                                     1, _("Create a sequence of paraxial line segments"),
3790                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3791                                     -1 );
3792             }
3794             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3795                                                                 "FreehandModeActionPencil" :
3796                                                                 "FreehandModeActionPen",
3797                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3798             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3800             ege_select_one_action_set_appearance( act, "full" );
3801             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3802             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3803             ege_select_one_action_set_icon_column( act, 2 );
3804             ege_select_one_action_set_icon_size( act, secondarySize );
3805             ege_select_one_action_set_tooltip_column( act, 1  );
3807             ege_select_one_action_set_active( act, freehandMode);
3808             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3809         }
3810     }
3813 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3814     gint shape = ege_select_one_action_get_active( act );
3815     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3816     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3819 /**
3820  * \brief Generate the list of freehand advanced shape option entries.
3821  */
3822 GList * freehand_shape_dropdown_items_list() {
3823     GList *glist = NULL;
3825     glist = g_list_append (glist, _("None"));
3826     glist = g_list_append (glist, _("Triangle in"));
3827     glist = g_list_append (glist, _("Triangle out"));
3828     glist = g_list_append (glist, _("Ellipse"));
3829     glist = g_list_append (glist, _("From clipboard"));
3831     return glist;
3834 static void
3835 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3836     /*advanced shape options */
3837     {
3838         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3839         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3841         GList* items = 0;
3842         gint count = 0;
3843         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3844         {
3845             GtkTreeIter iter;
3846             gtk_list_store_append( model, &iter );
3847             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3848             count++;
3849         }
3850         g_list_free( items );
3851         items = 0;
3852         EgeSelectOneAction* act1 = ege_select_one_action_new(
3853             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3854             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3855         g_object_set( act1, "short_label", _("Shape:"), NULL );
3856         ege_select_one_action_set_appearance( act1, "compact" );
3857         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3858         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3859         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3860         g_object_set_data( holder, "shape_action", act1 );
3861     }
3864 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3866     sp_add_freehand_mode_toggle(mainActions, holder, false);
3867     freehand_add_advanced_shape_options(mainActions, holder, false);
3871 static void
3872 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3874     GtkWidget *tbl = GTK_WIDGET(obj);
3876     GtkAdjustment *adj;
3878     // fixme: make settable
3879     gdouble tolerance = 4;
3881     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3882     gtk_adjustment_set_value(adj, tolerance);
3883     gtk_adjustment_value_changed(adj);
3885     spinbutton_defocus(GTK_OBJECT(tbl));
3888 static void
3889 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3891     // quit if run by the attr_changed listener
3892     if (g_object_get_data( tbl, "freeze" )) {
3893         return;
3894     }
3895     // in turn, prevent listener from responding
3896     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3897     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3898     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3899     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3902 /*
3903 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3904 public:
3905     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3906     {
3907         g_object_set_data(_obj, "prefobserver", this);
3908     }
3909     virtual ~PencilToleranceObserver() {
3910         if (g_object_get_data(_obj, "prefobserver") == this) {
3911             g_object_set_data(_obj, "prefobserver", NULL);
3912         }
3913     }
3914     virtual void notify(Inkscape::Preferences::Entry const &val) {
3915         GObject* tbl = _obj;
3916         if (g_object_get_data( tbl, "freeze" )) {
3917             return;
3918         }
3919         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3921         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3923         double v = val.getDouble(adj->value);
3924         gtk_adjustment_set_value(adj, v);
3925         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3926     }
3927 private:
3928     GObject *_obj;
3929 };
3930 */
3932 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3934     sp_add_freehand_mode_toggle(mainActions, holder, true);
3936     EgeAdjustmentAction* eact = 0;
3938     /* Tolerance */
3939     {
3940         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3941         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3942         eact = create_adjustment_action( "PencilToleranceAction",
3943                                          _("Smoothing:"), _("Smoothing: "),
3944                  _("How much smoothing (simplifying) is applied to the line"),
3945                                          "/tools/freehand/pencil/tolerance",
3946                                          3.0,
3947                                          GTK_WIDGET(desktop->canvas), NULL,
3948                                          holder, TRUE, "altx-pencil",
3949                                          1, 100.0, 0.5, 1.0,
3950                                          labels, values, G_N_ELEMENTS(labels),
3951                                          sp_pencil_tb_tolerance_value_changed,
3952                                          1, 2);
3953         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3954         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3955     }
3957     /* advanced shape options */
3958     freehand_add_advanced_shape_options(mainActions, holder, true);
3960     /* Reset */
3961     {
3962         InkAction* inky = ink_action_new( "PencilResetAction",
3963                                           _("Defaults"),
3964                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3965                                           GTK_STOCK_CLEAR,
3966                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3967         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3968         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3969     }
3971     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3976 //########################
3977 //##       Tweak        ##
3978 //########################
3980 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3982     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3983     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
3986 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3988     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3989     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
3992 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3994     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3995     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
3998 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4000     int mode = ege_select_one_action_get_active( act );
4001     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4002     prefs->setInt("/tools/tweak/mode", mode);
4004     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4005     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4006     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4007     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4008     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4009     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4010     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4011         if (doh) gtk_action_set_sensitive (doh, TRUE);
4012         if (dos) gtk_action_set_sensitive (dos, TRUE);
4013         if (dol) gtk_action_set_sensitive (dol, TRUE);
4014         if (doo) gtk_action_set_sensitive (doo, TRUE);
4015         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4016         if (fid) gtk_action_set_sensitive (fid, FALSE);
4017     } else {
4018         if (doh) gtk_action_set_sensitive (doh, FALSE);
4019         if (dos) gtk_action_set_sensitive (dos, FALSE);
4020         if (dol) gtk_action_set_sensitive (dol, FALSE);
4021         if (doo) gtk_action_set_sensitive (doo, FALSE);
4022         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4023         if (fid) gtk_action_set_sensitive (fid, TRUE);
4024     }
4027 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4029     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4030     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4033 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4034     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4035     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4037 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4038     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4039     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4041 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4042     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4043     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4045 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4046     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4047     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4050 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4052     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4053     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4055     {
4056         /* Width */
4057         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4058         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4059         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4060                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4061                                                               "/tools/tweak/width", 15,
4062                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4063                                                               1, 100, 1.0, 10.0,
4064                                                               labels, values, G_N_ELEMENTS(labels),
4065                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4066         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4067         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4068         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4069     }
4072     {
4073         /* Force */
4074         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4075         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4076         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4077                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4078                                                               "/tools/tweak/force", 20,
4079                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4080                                                               1, 100, 1.0, 10.0,
4081                                                               labels, values, G_N_ELEMENTS(labels),
4082                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4083         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4084         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4085         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4086     }
4088     /* Mode */
4089     {
4090         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4092         GtkTreeIter iter;
4093         gtk_list_store_append( model, &iter );
4094         gtk_list_store_set( model, &iter,
4095                             0, _("Move mode"),
4096                             1, _("Move objects in any direction"),
4097                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4098                             -1 );
4100         gtk_list_store_append( model, &iter );
4101         gtk_list_store_set( model, &iter,
4102                             0, _("Move in/out mode"),
4103                             1, _("Move objects towards cursor; with Shift from cursor"),
4104                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4105                             -1 );
4107         gtk_list_store_append( model, &iter );
4108         gtk_list_store_set( model, &iter,
4109                             0, _("Move jitter mode"),
4110                             1, _("Move objects in random directions"),
4111                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4112                             -1 );
4114         gtk_list_store_append( model, &iter );
4115         gtk_list_store_set( model, &iter,
4116                             0, _("Scale mode"),
4117                             1, _("Shrink objects, with Shift enlarge"),
4118                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4119                             -1 );
4121         gtk_list_store_append( model, &iter );
4122         gtk_list_store_set( model, &iter,
4123                             0, _("Rotate mode"),
4124                             1, _("Rotate objects, with Shift counterclockwise"),
4125                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4126                             -1 );
4128         gtk_list_store_append( model, &iter );
4129         gtk_list_store_set( model, &iter,
4130                             0, _("Duplicate/delete mode"),
4131                             1, _("Duplicate objects, with Shift delete"),
4132                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4133                             -1 );
4135         gtk_list_store_append( model, &iter );
4136         gtk_list_store_set( model, &iter,
4137                             0, _("Push mode"),
4138                             1, _("Push parts of paths in any direction"),
4139                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4140                             -1 );
4142         gtk_list_store_append( model, &iter );
4143         gtk_list_store_set( model, &iter,
4144                             0, _("Shrink/grow mode"),
4145                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4146                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4147                             -1 );
4149         gtk_list_store_append( model, &iter );
4150         gtk_list_store_set( model, &iter,
4151                             0, _("Attract/repel mode"),
4152                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4153                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4154                             -1 );
4156         gtk_list_store_append( model, &iter );
4157         gtk_list_store_set( model, &iter,
4158                             0, _("Roughen mode"),
4159                             1, _("Roughen parts of paths"),
4160                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4161                             -1 );
4163         gtk_list_store_append( model, &iter );
4164         gtk_list_store_set( model, &iter,
4165                             0, _("Color paint mode"),
4166                             1, _("Paint the tool's color upon selected objects"),
4167                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4168                             -1 );
4170         gtk_list_store_append( model, &iter );
4171         gtk_list_store_set( model, &iter,
4172                             0, _("Color jitter mode"),
4173                             1, _("Jitter the colors of selected objects"),
4174                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4175                             -1 );
4177         gtk_list_store_append( model, &iter );
4178         gtk_list_store_set( model, &iter,
4179                             0, _("Blur mode"),
4180                             1, _("Blur selected objects more; with Shift, blur less"),
4181                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4182                             -1 );
4185         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4186         g_object_set( act, "short_label", _("Mode:"), NULL );
4187         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4188         g_object_set_data( holder, "mode_action", act );
4190         ege_select_one_action_set_appearance( act, "full" );
4191         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4192         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4193         ege_select_one_action_set_icon_column( act, 2 );
4194         ege_select_one_action_set_icon_size( act, secondarySize );
4195         ege_select_one_action_set_tooltip_column( act, 1  );
4197         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4198         ege_select_one_action_set_active( act, mode );
4199         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4201         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4202     }
4204     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4206     {
4207         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4208         ege_output_action_set_use_markup( act, TRUE );
4209         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4210         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4211             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4212         g_object_set_data( holder, "tweak_channels_label", act);
4213     }
4215     {
4216         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4217                                                       _("Hue"),
4218                                                       _("In color mode, act on objects' hue"),
4219                                                       NULL,
4220                                                       Inkscape::ICON_SIZE_DECORATION );
4221         //TRANSLATORS:  "H" here stands for hue
4222         g_object_set( act, "short_label", _("H"), NULL );
4223         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4224         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4225         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4226         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4227             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4228         g_object_set_data( holder, "tweak_doh", act);
4229     }
4230     {
4231         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4232                                                       _("Saturation"),
4233                                                       _("In color mode, act on objects' saturation"),
4234                                                       NULL,
4235                                                       Inkscape::ICON_SIZE_DECORATION );
4236         //TRANSLATORS: "S" here stands for Saturation
4237         g_object_set( act, "short_label", _("S"), NULL );
4238         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4239         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4240         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4241         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4242             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4243         g_object_set_data( holder, "tweak_dos", act );
4244     }
4245     {
4246         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4247                                                       _("Lightness"),
4248                                                       _("In color mode, act on objects' lightness"),
4249                                                       NULL,
4250                                                       Inkscape::ICON_SIZE_DECORATION );
4251         //TRANSLATORS: "L" here stands for Lightness
4252         g_object_set( act, "short_label", _("L"), NULL );
4253         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4254         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4255         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4256         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4257             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4258         g_object_set_data( holder, "tweak_dol", act );
4259     }
4260     {
4261         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4262                                                       _("Opacity"),
4263                                                       _("In color mode, act on objects' opacity"),
4264                                                       NULL,
4265                                                       Inkscape::ICON_SIZE_DECORATION );
4266         //TRANSLATORS: "O" here stands for Opacity
4267         g_object_set( act, "short_label", _("O"), NULL );
4268         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4269         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4270         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4271         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4272             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4273         g_object_set_data( holder, "tweak_doo", act );
4274     }
4276     {   /* Fidelity */
4277         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4278         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4279         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4280                                                               _("Fidelity"), _("Fidelity:"),
4281                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4282                                                               "/tools/tweak/fidelity", 50,
4283                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4284                                                               1, 100, 1.0, 10.0,
4285                                                               labels, values, G_N_ELEMENTS(labels),
4286                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4287         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4288         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4289         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4290             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4291         g_object_set_data( holder, "tweak_fidelity", eact );
4292     }
4295     /* Use Pressure button */
4296     {
4297         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4298                                                       _("Pressure"),
4299                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4300                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4301                                                       Inkscape::ICON_SIZE_DECORATION );
4302         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4303         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4304         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4305     }
4310 //########################
4311 //##       Spray        ##
4312 //########################
4314 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4316     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4317     prefs->setDouble( "/tools/spray/width", adj->value );
4320 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4322     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4323     prefs->setDouble( "/tools/spray/mean", adj->value );
4326 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4328     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4329     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4332 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4334     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4335     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4338 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4340     int mode = ege_select_one_action_get_active( act );
4341     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4342     prefs->setInt("/tools/spray/mode", mode);
4345 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4347     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4348     prefs->setDouble( "/tools/spray/population", adj->value );
4351 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4353     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4354     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4357 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4359     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4360     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4364 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4366     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4367     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4369     {
4370         /* Width */
4371         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4372         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4373         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4374                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4375                                                               "/tools/spray/width", 15,
4376                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4377                                                               1, 100, 1.0, 10.0,
4378                                                               labels, values, G_N_ELEMENTS(labels),
4379                                                               sp_spray_width_value_changed,  1, 0 );
4380         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4381         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4382         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4383     }
4385     {
4386         /* Mean */
4387         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4388         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4389         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4390                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4391                                                               "/tools/spray/mean", 0,
4392                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4393                                                               0, 100, 1.0, 10.0,
4394                                                               labels, values, G_N_ELEMENTS(labels),
4395                                                               sp_spray_mean_value_changed,  1, 0 );
4396         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4397         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4398         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4399     }
4401     {
4402         /* Standard_deviation */
4403         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4404         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4405         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4406                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4407                                                               "/tools/spray/standard_deviation", 70,
4408                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4409                                                               1, 100, 1.0, 10.0,
4410                                                               labels, values, G_N_ELEMENTS(labels),
4411                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4412         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4413         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4414         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4415     }
4417     /* Mode */
4418     {
4419         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4421         GtkTreeIter iter;
4422         gtk_list_store_append( model, &iter );
4423         gtk_list_store_set( model, &iter,
4424                             0, _("Spray with copies"),
4425                             1, _("Spray copies of the initial selection"),
4426                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4427                             -1 );
4429         gtk_list_store_append( model, &iter );
4430         gtk_list_store_set( model, &iter,
4431                             0, _("Spray with clones"),
4432                             1, _("Spray clones of the initial selection"),
4433                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4434                             -1 );
4436         gtk_list_store_append( model, &iter );
4437         gtk_list_store_set( model, &iter,
4438                             0, _("Spray single path"),
4439                             1, _("Spray objects in a single path"),
4440                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4441                             -1 );
4443         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4444         g_object_set( act, "short_label", _("Mode:"), NULL );
4445         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4446         g_object_set_data( holder, "mode_action", act );
4448         ege_select_one_action_set_appearance( act, "full" );
4449         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4450         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4451         ege_select_one_action_set_icon_column( act, 2 );
4452         ege_select_one_action_set_icon_size( act, secondarySize );
4453         ege_select_one_action_set_tooltip_column( act, 1  );
4455         gint mode = prefs->getInt("/tools/spray/mode", 1);
4456         ege_select_one_action_set_active( act, mode );
4457         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4459         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4460     }
4462     {   /* Population */
4463         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4464         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4465         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4466                                                               _("Amount"), _("Amount:"),
4467                                                               _("Adjusts the number of items sprayed per clic."),
4468                                                               "/tools/spray/population", 70,
4469                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4470                                                               1, 100, 1.0, 10.0,
4471                                                               labels, values, G_N_ELEMENTS(labels),
4472                                                               sp_spray_population_value_changed,  1, 0 );
4473         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4474         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4475         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4476         g_object_set_data( holder, "spray_population", eact );
4477     }
4479     /* Use Pressure button */
4480     {
4481         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4482                                                       _("Pressure"),
4483                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4484                                                       "use_pressure",
4485                                                       Inkscape::ICON_SIZE_DECORATION );
4486         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4487         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4488         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4489     }
4491     {   /* Rotation */
4492         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4493         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4494         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4495                                                               _("Rotation"), _("Rotation:"),
4496                                                               // xgettext:no-c-format
4497                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4498                                                               "/tools/spray/rotation_variation", 0,
4499                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4500                                                               0, 100, 1.0, 10.0,
4501                                                               labels, values, G_N_ELEMENTS(labels),
4502                                                               sp_spray_rotation_value_changed,  1, 0 );
4503         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4504         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4505         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4506         g_object_set_data( holder, "spray_rotation", eact );
4507     }
4509     {   /* Scale */
4510         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4511         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4512         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4513                                                               _("Scale"), _("Scale:"),
4514                                                               // xgettext:no-c-format
4515                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4516                                                               "/tools/spray/scale_variation", 0,
4517                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4518                                                               0, 100, 1.0, 10.0,
4519                                                               labels, values, G_N_ELEMENTS(labels),
4520                                                               sp_spray_scale_value_changed,  1, 0 );
4521         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4522         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4523         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4524         g_object_set_data( holder, "spray_scale", eact );
4525     }
4532 //########################
4533 //##     Calligraphy    ##
4534 //########################
4535 static void update_presets_list (GObject *tbl)
4537     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4538     if (g_object_get_data(tbl, "presets_blocked"))
4539         return;
4541     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4542     if (!sel) {
4543         // WTF!? This will cause a segfault if ever reached
4544         //ege_select_one_action_set_active(sel, 0);
4545         return;
4546     }
4548     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4550     int ege_index = 1;
4551     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4552         bool match = true;
4554         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4555         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4556             Glib::ustring entry_name = j->getEntryName();
4557             if (entry_name == "id" || entry_name == "name") continue;
4559             void *widget = g_object_get_data(tbl, entry_name.data());
4560             if (widget) {
4561                 if (GTK_IS_ADJUSTMENT(widget)) {
4562                     double v = j->getDouble();
4563                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4564                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4565                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4566                         match = false;
4567                         break;
4568                     }
4569                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4570                     bool v = j->getBool();
4571                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4572                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4573                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4574                         match = false;
4575                         break;
4576                     }
4577                 }
4578             }
4579         }
4581         if (match) {
4582             // newly added item is at the same index as the
4583             // save command, so we need to change twice for it to take effect
4584             ege_select_one_action_set_active(sel, 0);
4585             ege_select_one_action_set_active(sel, ege_index); // one-based index
4586             return;
4587         }
4588     }
4590     // no match found
4591     ege_select_one_action_set_active(sel, 0);
4594 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4596     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4597     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4598     update_presets_list(tbl);
4601 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4603     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4604     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4605     update_presets_list(tbl);
4608 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4610     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4611     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4612     update_presets_list(tbl);
4615 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4617     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4618     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4619     update_presets_list(tbl);
4622 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4624     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4625     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4626     update_presets_list(tbl);
4629 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4631     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4632     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4633     update_presets_list(tbl);
4636 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4638     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4639     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4640     update_presets_list(tbl);
4643 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4645     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4646     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4647     update_presets_list(tbl);
4650 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4652     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4653     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4654     update_presets_list(tbl);
4657 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4659     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4660     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4661     update_presets_list(tbl);
4664 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4666     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4667     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4668     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4669     update_presets_list(tbl);
4670     if (calligraphy_angle )
4671         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4675 static gchar const *const widget_names[] = {
4676     "width",
4677     "mass",
4678     "wiggle",
4679     "angle",
4680     "thinning",
4681     "tremor",
4682     "flatness",
4683     "cap_rounding",
4684     "usepressure",
4685     "tracebackground",
4686     "usetilt"
4687 };
4690 static void sp_dcc_build_presets_list(GObject *tbl)
4692     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4694     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4695     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4696     gtk_list_store_clear (model);
4698     {
4699         GtkTreeIter iter;
4700         gtk_list_store_append( model, &iter );
4701         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4702     }
4704     // iterate over all presets to populate the list
4705     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4706     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4707     int ii=1;
4709     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4710         GtkTreeIter iter;
4711         Glib::ustring preset_name = prefs->getString(*i + "/name");
4712         gtk_list_store_append( model, &iter );
4713         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4714     }
4716     {
4717         GtkTreeIter iter;
4718         gtk_list_store_append( model, &iter );
4719         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4720         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4721     }
4723     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4725     update_presets_list (tbl);
4728 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4730     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4731     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4732     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4733     if (! desktop) return;
4735     if (g_object_get_data(tbl, "presets_blocked"))
4736         return;
4738     CalligraphicProfileRename::show(desktop);
4739     if ( !CalligraphicProfileRename::applied()) {
4740         // dialog cancelled
4741         update_presets_list (tbl);
4742         return;
4743     }
4744     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4746     if (profile_name.empty()) {
4747         // empty name entered
4748         update_presets_list (tbl);
4749         return;
4750     }
4752     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4754     // If there's a preset with the given name, find it and set save_path appropriately
4755     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4756     int total_presets = presets.size();
4757     int new_index = -1;
4758     Glib::ustring save_path; // profile pref path without a trailing slash
4760     int temp_index = 0;
4761     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4762         Glib::ustring name = prefs->getString(*i + "/name");
4763         if (!name.empty() && profile_name == name) {
4764             new_index = temp_index;
4765             save_path = *i;
4766             break;
4767         }
4768     }
4770     if (new_index == -1) {
4771         // no preset with this name, create
4772         new_index = total_presets + 1;
4773         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4774         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4775         g_free(profile_id);
4776     }
4778     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4779         gchar const *const widget_name = widget_names[i];
4780         void *widget = g_object_get_data(tbl, widget_name);
4781         if (widget) {
4782             if (GTK_IS_ADJUSTMENT(widget)) {
4783                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4784                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4785                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4786             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4787                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4788                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4789                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4790             } else {
4791                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4792             }
4793         } else {
4794             g_warning("Bad key when writing preset: %s\n", widget_name);
4795         }
4796     }
4797     prefs->setString(save_path + "/name", profile_name);
4799     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4800     sp_dcc_build_presets_list (tbl);
4804 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4806     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4808     gint preset_index = ege_select_one_action_get_active( act );
4809     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4810     // even when the preset is not changed. It would be good to replace it with something more
4811     // modern. Index 0 means "No preset", so we don't do anything.
4812     if (preset_index == 0) return;
4814     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4816     if (preset_index == save_presets_index) {
4817         // this is the Save command
4818         sp_dcc_save_profile(NULL, tbl);
4819         return;
4820     }
4822     if (g_object_get_data(tbl, "presets_blocked"))
4823         return;
4825     // preset_index is one-based so we subtract 1
4826     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4827     Glib::ustring preset_path = presets.at(preset_index - 1);
4829     if (!preset_path.empty()) {
4830         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE)); //temporarily block the selector so no one will updadte it while we're reading it
4832         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4834         // Shouldn't this be std::map?
4835         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4836             Glib::ustring entry_name = i->getEntryName();
4837             if (entry_name == "id" || entry_name == "name") continue;
4838             void *widget = g_object_get_data(tbl, entry_name.data());
4839             if (widget) {
4840                 if (GTK_IS_ADJUSTMENT(widget)) {
4841                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4842                     gtk_adjustment_set_value(adj, i->getDouble());
4843                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4844                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4845                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4846                     gtk_toggle_action_set_active(toggle, i->getBool());
4847                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4848                 } else {
4849                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4850                 }
4851             } else {
4852                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4853             }
4854         }
4855         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4856     }
4860 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4862     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4863     {
4864         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4866         EgeAdjustmentAction* calligraphy_angle = 0;
4868         {
4869         /* Width */
4870         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4871         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4872         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4873                                                               _("Pen Width"), _("Width:"),
4874                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4875                                                               "/tools/calligraphic/width", 15,
4876                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4877                                                               1, 100, 1.0, 10.0,
4878                                                               labels, values, G_N_ELEMENTS(labels),
4879                                                               sp_ddc_width_value_changed,  1, 0 );
4880         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4881         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4882         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4883         }
4885         {
4886         /* Thinning */
4887             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4888             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4889         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4890                                                               _("Stroke Thinning"), _("Thinning:"),
4891                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4892                                                               "/tools/calligraphic/thinning", 10,
4893                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4894                                                               -100, 100, 1, 10.0,
4895                                                               labels, values, G_N_ELEMENTS(labels),
4896                                                               sp_ddc_velthin_value_changed, 1, 0);
4897         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4898         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4899         }
4901         {
4902         /* Angle */
4903         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4904         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4905         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4906                                                               _("Pen Angle"), _("Angle:"),
4907                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4908                                                               "/tools/calligraphic/angle", 30,
4909                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4910                                                               -90.0, 90.0, 1.0, 10.0,
4911                                                               labels, values, G_N_ELEMENTS(labels),
4912                                                               sp_ddc_angle_value_changed, 1, 0 );
4913         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4914         g_object_set_data( holder, "angle_action", eact );
4915         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4916         calligraphy_angle = eact;
4917         }
4919         {
4920         /* Fixation */
4921             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4922         gdouble values[] = {0, 20, 40, 60, 90, 100};
4923         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4924                                                               _("Fixation"), _("Fixation:"),
4925                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4926                                                               "/tools/calligraphic/flatness", 90,
4927                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4928                                                               0.0, 100, 1.0, 10.0,
4929                                                               labels, values, G_N_ELEMENTS(labels),
4930                                                               sp_ddc_flatness_value_changed, 1, 0);
4931         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4932         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4933         }
4935         {
4936         /* Cap Rounding */
4937             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4938         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4939         // TRANSLATORS: "cap" means "end" (both start and finish) here
4940         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4941                                                               _("Cap rounding"), _("Caps:"),
4942                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4943                                                               "/tools/calligraphic/cap_rounding", 0.0,
4944                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4945                                                               0.0, 5.0, 0.01, 0.1,
4946                                                               labels, values, G_N_ELEMENTS(labels),
4947                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4948         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4949         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4950         }
4952         {
4953         /* Tremor */
4954             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4955         gdouble values[] = {0, 10, 20, 40, 60, 100};
4956         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4957                                                               _("Stroke Tremor"), _("Tremor:"),
4958                                                               _("Increase to make strokes rugged and trembling"),
4959                                                               "/tools/calligraphic/tremor", 0.0,
4960                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4961                                                               0.0, 100, 1, 10.0,
4962                                                               labels, values, G_N_ELEMENTS(labels),
4963                                                               sp_ddc_tremor_value_changed, 1, 0);
4965         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4966         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4967         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4968         }
4970         {
4971         /* Wiggle */
4972         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4973         gdouble values[] = {0, 20, 40, 60, 100};
4974         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4975                                                               _("Pen Wiggle"), _("Wiggle:"),
4976                                                               _("Increase to make the pen waver and wiggle"),
4977                                                               "/tools/calligraphic/wiggle", 0.0,
4978                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4979                                                               0.0, 100, 1, 10.0,
4980                                                               labels, values, G_N_ELEMENTS(labels),
4981                                                               sp_ddc_wiggle_value_changed, 1, 0);
4982         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4983         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4984         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4985         }
4987         {
4988         /* Mass */
4989             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4990         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4991         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4992                                                               _("Pen Mass"), _("Mass:"),
4993                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4994                                                               "/tools/calligraphic/mass", 2.0,
4995                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4996                                                               0.0, 100, 1, 10.0,
4997                                                               labels, values, G_N_ELEMENTS(labels),
4998                                                               sp_ddc_mass_value_changed, 1, 0);
4999         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5000         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5001         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5002         }
5005         /* Trace Background button */
5006         {
5007             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5008                                                           _("Trace Background"),
5009                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5010                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5011                                                           Inkscape::ICON_SIZE_DECORATION );
5012             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5013             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5014             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5015             g_object_set_data( holder, "tracebackground", act );
5016         }
5018         /* Use Pressure button */
5019         {
5020             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5021                                                           _("Pressure"),
5022                                                           _("Use the pressure of the input device to alter the width of the pen"),
5023                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5024                                                           Inkscape::ICON_SIZE_DECORATION );
5025             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5026             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5027             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5028             g_object_set_data( holder, "usepressure", act );
5029         }
5031         /* Use Tilt button */
5032         {
5033             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5034                                                           _("Tilt"),
5035                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5036                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5037                                                           Inkscape::ICON_SIZE_DECORATION );
5038             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5039             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5040             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5041             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5042             g_object_set_data( holder, "usetilt", act );
5043         }
5045         /*calligraphic profile */
5046         {
5047             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5048             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5049             ege_select_one_action_set_appearance (act1, "compact");
5050             g_object_set_data (holder, "profile_selector", act1 );
5052             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5054             sp_dcc_build_presets_list (holder);
5056             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5057             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5058         }
5059     }
5063 //########################
5064 //##    Circle / Arc    ##
5065 //########################
5067 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5069     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5070     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5072     if (v1 == 0 && v2 == 0) {
5073         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5074             gtk_action_set_sensitive( ocb, FALSE );
5075             gtk_action_set_sensitive( make_whole, FALSE );
5076         }
5077     } else {
5078         gtk_action_set_sensitive( ocb, TRUE );
5079         gtk_action_set_sensitive( make_whole, TRUE );
5080     }
5083 static void
5084 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5086     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5088     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5089         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5090         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5091     }
5093     // quit if run by the attr_changed listener
5094     if (g_object_get_data( tbl, "freeze" )) {
5095         return;
5096     }
5098     // in turn, prevent listener from responding
5099     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5101     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5103     bool modmade = false;
5104     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5105          items != NULL;
5106          items = items->next)
5107     {
5108         SPItem *item = SP_ITEM(items->data);
5110         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5112             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5113             SPArc *arc = SP_ARC(item);
5115             if (!strcmp(value_name, "start"))
5116                 ge->start = (adj->value * M_PI)/ 180;
5117             else
5118                 ge->end = (adj->value * M_PI)/ 180;
5120             sp_genericellipse_normalize(ge);
5121             ((SPObject *)arc)->updateRepr();
5122             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5124             modmade = true;
5125         }
5126     }
5128     g_free(namespaced_name);
5130     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5132     sp_arctb_sensitivize( tbl, adj->value, other->value );
5134     if (modmade) {
5135         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5136                                    _("Arc: Change start/end"));
5137     }
5139     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5143 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5145     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5148 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5150     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5154 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5156     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5157     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5158         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5159         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5160     }
5162     // quit if run by the attr_changed listener
5163     if (g_object_get_data( tbl, "freeze" )) {
5164         return;
5165     }
5167     // in turn, prevent listener from responding
5168     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5170     bool modmade = false;
5172     if ( ege_select_one_action_get_active(act) != 0 ) {
5173         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5174              items != NULL;
5175              items = items->next)
5176         {
5177             if (SP_IS_ARC((SPItem *) items->data)) {
5178                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5179                 repr->setAttribute("sodipodi:open", "true");
5180                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5181                 modmade = true;
5182             }
5183         }
5184     } else {
5185         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5186              items != NULL;
5187              items = items->next)
5188         {
5189             if (SP_IS_ARC((SPItem *) items->data))    {
5190                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5191                 repr->setAttribute("sodipodi:open", NULL);
5192                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5193                 modmade = true;
5194             }
5195         }
5196     }
5198     if (modmade) {
5199         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5200                                    _("Arc: Change open/closed"));
5201     }
5203     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5206 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5208     GtkAdjustment *adj;
5209     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5210     gtk_adjustment_set_value(adj, 0.0);
5211     gtk_adjustment_value_changed(adj);
5213     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5214     gtk_adjustment_set_value(adj, 0.0);
5215     gtk_adjustment_value_changed(adj);
5217     spinbutton_defocus( GTK_OBJECT(obj) );
5220 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5221                                       gchar const */*old_value*/, gchar const */*new_value*/,
5222                                       bool /*is_interactive*/, gpointer data)
5224     GObject *tbl = G_OBJECT(data);
5226     // quit if run by the _changed callbacks
5227     if (g_object_get_data( tbl, "freeze" )) {
5228         return;
5229     }
5231     // in turn, prevent callbacks from responding
5232     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5234     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5235     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5237     GtkAdjustment *adj1,*adj2;
5238     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5239     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5240     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5241     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5243     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5245     char const *openstr = NULL;
5246     openstr = repr->attribute("sodipodi:open");
5247     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5249     if (openstr) {
5250         ege_select_one_action_set_active( ocb, 1 );
5251     } else {
5252         ege_select_one_action_set_active( ocb, 0 );
5253     }
5255     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5258 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5259     NULL, /* child_added */
5260     NULL, /* child_removed */
5261     arc_tb_event_attr_changed,
5262     NULL, /* content_changed */
5263     NULL  /* order_changed */
5264 };
5267 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5269     int n_selected = 0;
5270     Inkscape::XML::Node *repr = NULL;
5272     purge_repr_listener( tbl, tbl );
5274     for (GSList const *items = selection->itemList();
5275          items != NULL;
5276          items = items->next)
5277     {
5278         if (SP_IS_ARC((SPItem *) items->data)) {
5279             n_selected++;
5280             repr = SP_OBJECT_REPR((SPItem *) items->data);
5281         }
5282     }
5284     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5286     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5287     if (n_selected == 0) {
5288         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5289     } else if (n_selected == 1) {
5290         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5291         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5293         if (repr) {
5294             g_object_set_data( tbl, "repr", repr );
5295             Inkscape::GC::anchor(repr);
5296             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5297             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5298         }
5299     } else {
5300         // FIXME: implement averaging of all parameters for multiple selected
5301         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5302         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5303         sp_arctb_sensitivize( tbl, 1, 0 );
5304     }
5308 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5310     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5312     EgeAdjustmentAction* eact = 0;
5313     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5316     {
5317         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5318         ege_output_action_set_use_markup( act, TRUE );
5319         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5320         g_object_set_data( holder, "mode_action", act );
5321     }
5323     /* Start */
5324     {
5325         eact = create_adjustment_action( "ArcStartAction",
5326                                          _("Start"), _("Start:"),
5327                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5328                                          "/tools/shapes/arc/start", 0.0,
5329                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5330                                          -360.0, 360.0, 1.0, 10.0,
5331                                          0, 0, 0,
5332                                          sp_arctb_start_value_changed);
5333         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5334     }
5336     /* End */
5337     {
5338         eact = create_adjustment_action( "ArcEndAction",
5339                                          _("End"), _("End:"),
5340                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5341                                          "/tools/shapes/arc/end", 0.0,
5342                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5343                                          -360.0, 360.0, 1.0, 10.0,
5344                                          0, 0, 0,
5345                                          sp_arctb_end_value_changed);
5346         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5347     }
5349     /* Segments / Pie checkbox */
5350     {
5351         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5353         GtkTreeIter iter;
5354         gtk_list_store_append( model, &iter );
5355         gtk_list_store_set( model, &iter,
5356                             0, _("Closed arc"),
5357                             1, _("Switch to segment (closed shape with two radii)"),
5358                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5359                             -1 );
5361         gtk_list_store_append( model, &iter );
5362         gtk_list_store_set( model, &iter,
5363                             0, _("Open Arc"),
5364                             1, _("Switch to arc (unclosed shape)"),
5365                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5366                             -1 );
5368         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5369         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5370         g_object_set_data( holder, "open_action", act );
5372         ege_select_one_action_set_appearance( act, "full" );
5373         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5374         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5375         ege_select_one_action_set_icon_column( act, 2 );
5376         ege_select_one_action_set_icon_size( act, secondarySize );
5377         ege_select_one_action_set_tooltip_column( act, 1  );
5379         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5380         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5381         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5382     }
5384     /* Make Whole */
5385     {
5386         InkAction* inky = ink_action_new( "ArcResetAction",
5387                                           _("Make whole"),
5388                                           _("Make the shape a whole ellipse, not arc or segment"),
5389                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5390                                           secondarySize );
5391         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5392         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5393         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5394         g_object_set_data( holder, "make_whole", inky );
5395     }
5397     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5398     // sensitivize make whole and open checkbox
5399     {
5400         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5401         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5402         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5403     }
5406     sigc::connection *connection = new sigc::connection(
5407         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5408         );
5409     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5410     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5416 // toggle button callbacks and updaters
5418 //########################
5419 //##      Dropper       ##
5420 //########################
5422 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5423     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5424     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5425     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5426     if ( set_action ) {
5427         if ( gtk_toggle_action_get_active( act ) ) {
5428             gtk_action_set_sensitive( set_action, TRUE );
5429         } else {
5430             gtk_action_set_sensitive( set_action, FALSE );
5431         }
5432     }
5434     spinbutton_defocus(GTK_OBJECT(tbl));
5437 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5438     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5439     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5440     spinbutton_defocus(GTK_OBJECT(tbl));
5444 /**
5445  * Dropper auxiliary toolbar construction and setup.
5446  *
5447  * TODO: Would like to add swatch of current color.
5448  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5449  *       can drag and drop places. Will provide a nice mixing palette.
5450  */
5451 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5453     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5454     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5456     {
5457         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5458         ege_output_action_set_use_markup( act, TRUE );
5459         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5460     }
5462     {
5463         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5464                                                       _("Pick opacity"),
5465                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5466                                                       NULL,
5467                                                       Inkscape::ICON_SIZE_DECORATION );
5468         g_object_set( act, "short_label", _("Pick"), NULL );
5469         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5470         g_object_set_data( holder, "pick_action", act );
5471         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5472         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5473     }
5475     {
5476         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5477                                                       _("Assign opacity"),
5478                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5479                                                       NULL,
5480                                                       Inkscape::ICON_SIZE_DECORATION );
5481         g_object_set( act, "short_label", _("Assign"), NULL );
5482         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5483         g_object_set_data( holder, "set_action", act );
5484         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5485         // make sure it's disabled if we're not picking alpha
5486         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5487         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5488     }
5492 //########################
5493 //##      LPETool       ##
5494 //########################
5496 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5498 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5499 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5501     using namespace Inkscape::LivePathEffect;
5503     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5504     SPEventContext *ec = desktop->event_context;
5505     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5506         return;
5507     }
5509     // only take action if run by the attr_changed listener
5510     if (!g_object_get_data(tbl, "freeze")) {
5511         // in turn, prevent listener from responding
5512         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5514         gint mode = ege_select_one_action_get_active(act);
5515         EffectType type = lpesubtools[mode].type;
5517         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5518         bool success = lpetool_try_construction(lc, type);
5519         if (success) {
5520             // since the construction was already performed, we set the state back to inactive
5521             ege_select_one_action_set_active(act, 0);
5522             mode = 0;
5523         } else {
5524             // switch to the chosen subtool
5525             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5526         }
5528         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5529             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5530             prefs->setInt( "/tools/lpetool/mode", mode );
5531         }
5533         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5534     }
5537 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5539     SPEventContext *ec = selection->desktop()->event_context;
5540     if (!SP_IS_LPETOOL_CONTEXT(ec))
5541         return;
5543     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5546 void
5547 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5549     using namespace Inkscape::LivePathEffect;
5550     SPEventContext *ec = selection->desktop()->event_context;
5551     if (!SP_IS_LPETOOL_CONTEXT(ec))
5552         return;
5553     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5555     lpetool_delete_measuring_items(lc);
5556     lpetool_create_measuring_items(lc, selection);
5558     // activate line segment combo box if a single item with LPELineSegment is selected
5559     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5560     SPItem *item = selection->singleItem();
5561     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5562         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5563         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5564         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5565             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5566             g_object_set_data(tbl, "currentlpe", lpe);
5567             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5568             gtk_action_set_sensitive(w, TRUE);
5569             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5570         } else {
5571             g_object_set_data(tbl, "currentlpe", NULL);
5572             g_object_set_data(tbl, "currentlpeitem", NULL);
5573             gtk_action_set_sensitive(w, FALSE);
5574         }
5575     } else {
5576         g_object_set_data(tbl, "currentlpe", NULL);
5577         g_object_set_data(tbl, "currentlpeitem", NULL);
5578         gtk_action_set_sensitive(w, FALSE);
5579     }
5582 static void
5583 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5584     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5585     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5587     bool show = gtk_toggle_action_get_active( act );
5588     prefs->setBool("/tools/lpetool/show_bbox",  show);
5590     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5591         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5592         lpetool_context_reset_limiting_bbox(lc);
5593     }
5596 static void
5597 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5598     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5599     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5600         return;
5602     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5603     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5604     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5605         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5606         bool show = gtk_toggle_action_get_active( act );
5607         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5608         lpetool_show_measuring_info(lc, show);
5609         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5610     }
5613 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5614     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5615     SPUnit const *unit = tracker->getActiveUnit();
5616     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5617     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5619     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5620     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5621         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5622         lpetool_delete_measuring_items(lc);
5623         lpetool_create_measuring_items(lc);
5624     }
5627 static void
5628 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5629     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5630     Inkscape::Selection *selection = desktop->selection;
5632     Geom::OptRect bbox = selection->bounds();
5634     if (bbox) {
5635         Geom::Point A(bbox->min());
5636         Geom::Point B(bbox->max());
5638         A *= desktop->doc2dt();
5639         B *= desktop->doc2dt();
5641         // TODO: should we provide a way to store points in prefs?
5642         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5643         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5644         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5645         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5646         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5648         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5649     }
5651     gtk_toggle_action_set_active(act, false);
5654 static void
5655 sp_line_segment_build_list(GObject *tbl)
5657     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5659     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5660     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5661     gtk_list_store_clear (model);
5663     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5664     {
5665         GtkTreeIter iter;
5666         gtk_list_store_append( model, &iter );
5667         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5668         gtk_list_store_append( model, &iter );
5669         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5670         gtk_list_store_append( model, &iter );
5671         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5672         gtk_list_store_append( model, &iter );
5673         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5674     }
5676     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5679 static void
5680 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5681     using namespace Inkscape::LivePathEffect;
5683     // quit if run by the attr_changed listener
5684     if (g_object_get_data(tbl, "freeze")) {
5685         return;
5686     }
5688     // in turn, prevent listener from responding
5689     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5691     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5692     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5693     if (lpeitem) {
5694         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5695         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5696         sp_lpe_item_update_patheffect(lpeitem, true, true);
5697     }
5699     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5702 static void
5703 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5704     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5706     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5707         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5708     }
5709     gtk_toggle_action_set_active(act, false);
5712 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5714     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5715     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5716     g_object_set_data(holder, "tracker", tracker);
5717     SPUnit const *unit = tracker->getActiveUnit();
5719     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5720     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5722     /** Automatically create a list of LPEs that get added to the toolbar **/
5723     {
5724         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5726         GtkTreeIter iter;
5728         // the first toggle button represents the state that no subtool is active (remove this when
5729         // this can be modeled by EgeSelectOneAction or some other action)
5730         gtk_list_store_append( model, &iter );
5731         gtk_list_store_set( model, &iter,
5732                             0, _("All inactive"),
5733                             1, _("No geometric tool is active"),
5734                             2, "draw-geometry-inactive",
5735                             -1 );
5737         Inkscape::LivePathEffect::EffectType type;
5738         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5739             type =  lpesubtools[i].type;
5740             gtk_list_store_append( model, &iter );
5741             gtk_list_store_set( model, &iter,
5742                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5743                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5744                                 2, lpesubtools[i].icon_name,
5745                                 -1 );
5746         }
5748         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5749         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5750         g_object_set_data( holder, "lpetool_mode_action", act );
5752         ege_select_one_action_set_appearance( act, "full" );
5753         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5754         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5755         ege_select_one_action_set_icon_column( act, 2 );
5756         ege_select_one_action_set_tooltip_column( act, 1  );
5758         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5759         ege_select_one_action_set_active( act, lpeToolMode );
5760         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5761     }
5763     /* Show limiting bounding box */
5764     {
5765         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5766                                                       _("Show limiting bounding box"),
5767                                                       _("Show bounding box (used to cut infinite lines)"),
5768                                                       "show-bounding-box",
5769                                                       Inkscape::ICON_SIZE_DECORATION );
5770         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5771         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5772         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5773     }
5775     /* Set limiting bounding box to bbox of current selection */
5776     {
5777         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5778                                                       _("Get limiting bounding box from selection"),
5779                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5780                                                       "draw-geometry-set-bounding-box",
5781                                                       Inkscape::ICON_SIZE_DECORATION );
5782         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5783         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5784         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5785     }
5788     /* Combo box to choose line segment type */
5789     {
5790         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5791         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5792         ege_select_one_action_set_appearance (act, "compact");
5793         g_object_set_data (holder, "lpetool_line_segment_action", act );
5795         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5797         sp_line_segment_build_list (holder);
5799         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5800         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5801         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5802     }
5804     /* Display measuring info for selected items */
5805     {
5806         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5807                                                       _("Display measuring info"),
5808                                                       _("Display measuring info for selected items"),
5809                                                       "draw-geometry-show-measuring-info",
5810                                                       Inkscape::ICON_SIZE_DECORATION );
5811         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5812         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5813         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5814     }
5816     // add the units menu
5817     {
5818         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5819         gtk_action_group_add_action( mainActions, act );
5820         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5821         g_object_set_data(holder, "lpetool_units_action", act);
5822         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5823     }
5825     /* Open LPE dialog (to adapt parameters numerically) */
5826     {
5827         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5828                                                       _("Open LPE dialog"),
5829                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5830                                                       "dialog-geometry",
5831                                                       Inkscape::ICON_SIZE_DECORATION );
5832         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5833         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5834         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5835     }
5837     //watch selection
5838     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5840     sigc::connection *c_selection_modified =
5841         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5842                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5843     pool->add_connection ("selection-modified", c_selection_modified);
5845     sigc::connection *c_selection_changed =
5846         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5847                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5848     pool->add_connection ("selection-changed", c_selection_changed);
5851 //########################
5852 //##       Eraser       ##
5853 //########################
5855 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5857     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5858     prefs->setDouble( "/tools/eraser/width", adj->value );
5859     update_presets_list(tbl);
5862 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5864     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5865     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5866     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5867         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5868         prefs->setBool( "/tools/eraser/mode", eraserMode );
5869     }
5871     // only take action if run by the attr_changed listener
5872     if (!g_object_get_data( tbl, "freeze" )) {
5873         // in turn, prevent listener from responding
5874         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5876         if ( eraserMode != 0 ) {
5877         } else {
5878         }
5879         // TODO finish implementation
5881         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5882     }
5885 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5887     {
5888         /* Width */
5889         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5890         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5891         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5892                                                               _("Pen Width"), _("Width:"),
5893                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5894                                                               "/tools/eraser/width", 15,
5895                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5896                                                               1, 100, 1.0, 10.0,
5897                                                               labels, values, G_N_ELEMENTS(labels),
5898                                                               sp_erc_width_value_changed, 1, 0);
5899         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5900         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5901         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5902     }
5904     {
5905         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5907         GtkTreeIter iter;
5908         gtk_list_store_append( model, &iter );
5909         gtk_list_store_set( model, &iter,
5910                             0, _("Delete"),
5911                             1, _("Delete objects touched by the eraser"),
5912                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5913                             -1 );
5915         gtk_list_store_append( model, &iter );
5916         gtk_list_store_set( model, &iter,
5917                             0, _("Cut"),
5918                             1, _("Cut out from objects"),
5919                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5920                             -1 );
5922         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5923         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5924         g_object_set_data( holder, "eraser_mode_action", act );
5926         ege_select_one_action_set_appearance( act, "full" );
5927         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5928         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5929         ege_select_one_action_set_icon_column( act, 2 );
5930         ege_select_one_action_set_tooltip_column( act, 1  );
5932         /// @todo Convert to boolean?
5933         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5934         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5935         ege_select_one_action_set_active( act, eraserMode );
5936         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5937     }
5941 //########################
5942 //##    Text Toolbox    ##
5943 //########################
5944 /*
5945 static void
5946 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5948     //Call back for letter sizing spinbutton
5951 static void
5952 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5954     //Call back for line height spinbutton
5957 static void
5958 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5960     //Call back for horizontal kerning spinbutton
5963 static void
5964 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5966     //Call back for vertical kerning spinbutton
5969 static void
5970 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5972     //Call back for letter rotation spinbutton
5973 }*/
5975 namespace {
5977 void
5978 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5980     // quit if run by the _changed callbacks
5981     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5982         return;
5983     }
5985     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5987     SPStyle *query =
5988         sp_style_new (SP_ACTIVE_DOCUMENT);
5990     int result_family =
5991         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5993     int result_style =
5994         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5996     int result_numbers =
5997         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5999     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6001     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6002     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6003         // there are no texts in selection, read from prefs
6005         sp_style_read_from_prefs(query, "/tools/text");
6007         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6008             // do not reset the toolbar style from prefs if we already did it last time
6009             sp_style_unref(query);
6010             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6011             return;
6012         }
6013         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6014     } else {
6015         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6016     }
6018     if (query->text)
6019     {
6020         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6021             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6022             gtk_entry_set_text (GTK_ENTRY (entry), "");
6024         } else if (query->text->font_specification.value || query->text->font_family.value) {
6026             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6027             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6029             // Get the font that corresponds
6030             Glib::ustring familyName;
6032             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6033             if (font) {
6034                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6035                 font->Unref();
6036                 font = NULL;
6037             }
6039             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6041             Gtk::TreeIter iter;
6042             try {
6043                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6044                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6045                 iter = model->get_iter(path);
6046             } catch (...) {
6047                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6048                 sp_style_unref(query);
6049                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6050                 return;
6051             }
6053             combo->set_active (iter);
6054         }
6056         //Size
6057         {
6058             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6059             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6060             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6061             g_free(str);
6062         }
6064         //Anchor
6065         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6066         {
6067             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6068             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6069             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6070             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6071         }
6072         else
6073         {
6074             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6075             {
6076                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6077                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6078                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6079                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6080             }
6081             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6082             {
6083                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6084                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6085                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6086                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6087             }
6088             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6089             {
6090                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6091                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6092                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6093                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6094             }
6095         }
6097         //Style
6098         {
6099             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6101             gboolean active = gtk_toggle_button_get_active (button);
6102             gboolean check  = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_NORMAL) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_LIGHTER));
6104             if (active != check)
6105             {
6106                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6107                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6108                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6109             }
6110         }
6112         {
6113             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6115             gboolean active = gtk_toggle_button_get_active (button);
6116             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6118             if (active != check)
6119             {
6120                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6121                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6122                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6123             }
6124         }
6126         //Orientation
6127         //locking both buttons, changing one affect all group (both)
6128         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6129         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6131         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6132         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6134         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6135         {
6136             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6137         }
6138         else
6139         {
6140             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6141         }
6142         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6143         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6144     }
6146     sp_style_unref(query);
6148     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6151 void
6152 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6154     sp_text_toolbox_selection_changed (selection, tbl);
6157 void
6158 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6160     sp_text_toolbox_selection_changed (NULL, tbl);
6163 void
6164 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6165                                 GObject             *tbl)
6167     // quit if run by the _changed callbacks
6168     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6169         return;
6170     }
6172     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6174     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6175     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6176     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6178     //g_print ("family changed to: %s\n", family);
6180     SPStyle *query =
6181         sp_style_new (SP_ACTIVE_DOCUMENT);
6183     int result_fontspec =
6184         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6186     SPCSSAttr *css = sp_repr_css_attr_new ();
6188     // First try to get the font spec from the stored value
6189     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6191     if (fontSpec.empty()) {
6192         // Construct a new font specification if it does not yet exist
6193         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6194         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6195         fontFromStyle->Unref();
6196     }
6198     if (!fontSpec.empty()) {
6200         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6202         if (!newFontSpec.empty()) {
6204             if (fontSpec != newFontSpec) {
6206                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6208                 if (font) {
6209                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6211                     // Set all the these just in case they were altered when finding the best
6212                     // match for the new family and old style...
6214                     gchar c[256];
6216                     font->Family(c, 256);
6218                     sp_repr_css_set_property (css, "font-family", c);
6220                     font->Attribute( "weight", c, 256);
6221                     sp_repr_css_set_property (css, "font-weight", c);
6223                     font->Attribute("style", c, 256);
6224                     sp_repr_css_set_property (css, "font-style", c);
6226                     font->Attribute("stretch", c, 256);
6227                     sp_repr_css_set_property (css, "font-stretch", c);
6229                     font->Attribute("variant", c, 256);
6230                     sp_repr_css_set_property (css, "font-variant", c);
6232                     font->Unref();
6233                 }
6234             }
6236         } else {
6237             // If the old font on selection (or default) was not existing on the system,
6238             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6239             // setting the family reported by the family chooser.
6241             //g_print ("fallback setting family: %s\n", family);
6242             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6243             sp_repr_css_set_property (css, "font-family", family);
6244         }
6245     }
6247     // If querying returned nothing, set the default style of the tool (for new texts)
6248     if (result_fontspec == QUERY_STYLE_NOTHING)
6249     {
6250         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6251         prefs->mergeStyle("/tools/text/style", css);
6252         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6253     }
6254     else
6255     {
6256         sp_desktop_set_style (desktop, css, true, true);
6257     }
6259     sp_style_unref(query);
6261     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6262                                    _("Text: Change font family"));
6263     sp_repr_css_attr_unref (css);
6265     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6267     // unfreeze
6268     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6270     // focus to canvas
6271     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6275 void
6276 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6277                                    gpointer          data)
6279     if (g_object_get_data (G_OBJECT (button), "block")) return;
6280     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6281     int prop = GPOINTER_TO_INT(data);
6283     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6285     // move the x of all texts to preserve the same bbox
6286     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6287     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6288         if (SP_IS_TEXT((SPItem *) items->data)) {
6289             SPItem *item = SP_ITEM(items->data);
6291             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6292             // below, variable names suggest horizontal move, but we check the writing direction
6293             // and move in the corresponding axis
6294             int axis;
6295             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6296                 axis = NR::X;
6297             } else {
6298                 axis = NR::Y;
6299             }
6301             Geom::OptRect bbox
6302                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6303             if (!bbox)
6304                 continue;
6305             double width = bbox->dimensions()[axis];
6306             // If you want to align within some frame, other than the text's own bbox, calculate
6307             // the left and right (or top and bottom for tb text) slacks of the text inside that
6308             // frame (currently unused)
6309             double left_slack = 0;
6310             double right_slack = 0;
6311             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6312             double move = 0;
6313             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6314                 switch (prop) {
6315                     case 0:
6316                         move = -left_slack;
6317                         break;
6318                     case 1:
6319                         move = width/2 + (right_slack - left_slack)/2;
6320                         break;
6321                     case 2:
6322                         move = width + right_slack;
6323                         break;
6324                 }
6325             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6326                 switch (prop) {
6327                     case 0:
6328                         move = -width/2 - left_slack;
6329                         break;
6330                     case 1:
6331                         move = (right_slack - left_slack)/2;
6332                         break;
6333                     case 2:
6334                         move = width/2 + right_slack;
6335                         break;
6336                 }
6337             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6338                 switch (prop) {
6339                     case 0:
6340                         move = -width - left_slack;
6341                         break;
6342                     case 1:
6343                         move = -width/2 + (right_slack - left_slack)/2;
6344                         break;
6345                     case 2:
6346                         move = right_slack;
6347                         break;
6348                 }
6349             }
6350             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6351             if (axis == NR::X) {
6352                 XY = XY + Geom::Point (move, 0);
6353             } else {
6354                 XY = XY + Geom::Point (0, move);
6355             }
6356             SP_TEXT(item)->attributes.setFirstXY(XY);
6357             SP_OBJECT(item)->updateRepr();
6358             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6359         }
6360     }
6362     SPCSSAttr *css = sp_repr_css_attr_new ();
6363     switch (prop)
6364     {
6365         case 0:
6366         {
6367             sp_repr_css_set_property (css, "text-anchor", "start");
6368             sp_repr_css_set_property (css, "text-align", "start");
6369             break;
6370         }
6371         case 1:
6372         {
6373             sp_repr_css_set_property (css, "text-anchor", "middle");
6374             sp_repr_css_set_property (css, "text-align", "center");
6375             break;
6376         }
6378         case 2:
6379         {
6380             sp_repr_css_set_property (css, "text-anchor", "end");
6381             sp_repr_css_set_property (css, "text-align", "end");
6382             break;
6383         }
6385         case 3:
6386         {
6387             sp_repr_css_set_property (css, "text-anchor", "start");
6388             sp_repr_css_set_property (css, "text-align", "justify");
6389             break;
6390         }
6391     }
6393     SPStyle *query =
6394         sp_style_new (SP_ACTIVE_DOCUMENT);
6395     int result_numbers =
6396         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6398     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6399     if (result_numbers == QUERY_STYLE_NOTHING)
6400     {
6401         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6402         prefs->mergeStyle("/tools/text/style", css);
6403     }
6405     sp_style_unref(query);
6407     sp_desktop_set_style (desktop, css, true, true);
6408     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6409                                    _("Text: Change alignment"));
6410     sp_repr_css_attr_unref (css);
6412     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6415 void
6416 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6417                                gpointer          data)
6419     if (g_object_get_data (G_OBJECT (button), "block")) return;
6421     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6422     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6423     int          prop       = GPOINTER_TO_INT(data);
6424     bool         active     = gtk_toggle_button_get_active (button);
6426     SPStyle *query =
6427         sp_style_new (SP_ACTIVE_DOCUMENT);
6429     int result_fontspec =
6430         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6432     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6433     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6434     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6436     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6437     Glib::ustring newFontSpec = "";
6439     if (fontSpec.empty()) {
6440         // Construct a new font specification if it does not yet exist
6441         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6442         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6443         fontFromStyle->Unref();
6444     }
6446     bool nochange = true;
6447     switch (prop)
6448     {
6449         case 0:
6450         {
6451             if (!fontSpec.empty()) {
6452                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6453                 if (!newFontSpec.empty()) {
6454                     // Don't even set the bold if the font didn't exist on the system
6455                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6456                     nochange = false;
6457                 }
6458             }
6459             // set or reset the button according
6460             if(nochange) {
6461                 gboolean check = gtk_toggle_button_get_active (button);
6463                 if (active != check)
6464                 {
6465                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6466                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6467                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6468                 }
6469             }
6471             break;
6472         }
6474         case 1:
6475         {
6476             if (!fontSpec.empty()) {
6477                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6478                 if (!newFontSpec.empty()) {
6479                     // Don't even set the italic if the font didn't exist on the system
6480                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6481                     nochange = false;
6482                 }
6483             }
6484             if(nochange) {
6485                 gboolean check = gtk_toggle_button_get_active (button);
6487                 if (active != check)
6488                 {
6489                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6490                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6491                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6492                 }
6493             }
6494             break;
6495         }
6496     }
6498     if (!newFontSpec.empty()) {
6499         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6500     }
6502     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6503     if (result_fontspec == QUERY_STYLE_NOTHING)
6504     {
6505         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6506         prefs->mergeStyle("/tools/text/style", css);
6507     }
6509     sp_style_unref(query);
6511     sp_desktop_set_style (desktop, css, true, true);
6512     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6513                                    _("Text: Change font style"));
6514     sp_repr_css_attr_unref (css);
6516     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6519 void
6520 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6521                                      gpointer         data)
6523     if (g_object_get_data (G_OBJECT (button), "block")) {
6524         return;
6525     }
6527     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6528     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6529     int          prop       = GPOINTER_TO_INT(data);
6531     switch (prop)
6532     {
6533         case 0:
6534         {
6535             sp_repr_css_set_property (css, "writing-mode", "lr");
6536             break;
6537         }
6539         case 1:
6540         {
6541             sp_repr_css_set_property (css, "writing-mode", "tb");
6542             break;
6543         }
6544     }
6546     SPStyle *query =
6547         sp_style_new (SP_ACTIVE_DOCUMENT);
6548     int result_numbers =
6549         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6551     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6552     if (result_numbers == QUERY_STYLE_NOTHING)
6553     {
6554         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6555         prefs->mergeStyle("/tools/text/style", css);
6556     }
6558     sp_desktop_set_style (desktop, css, true, true);
6559     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6560                                    _("Text: Change orientation"));
6561     sp_repr_css_attr_unref (css);
6563     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6566 gboolean
6567 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6569     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6570     if (!desktop) return FALSE;
6572     switch (get_group0_keyval (event)) {
6573         case GDK_KP_Enter: // chosen
6574         case GDK_Return:
6575             // unfreeze and update, which will defocus
6576             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6577             sp_text_toolbox_family_changed (NULL, tbl);
6578             return TRUE; // I consumed the event
6579             break;
6580         case GDK_Escape:
6581             // defocus
6582             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6583             return TRUE; // I consumed the event
6584             break;
6585     }
6586     return FALSE;
6589 gboolean
6590 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6592     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6593     if (!desktop) return FALSE;
6595     switch (get_group0_keyval (event)) {
6596         case GDK_KP_Enter:
6597         case GDK_Return:
6598         case GDK_Escape: // defocus
6599             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6600             return TRUE; // I consumed the event
6601             break;
6602         case GDK_w:
6603         case GDK_W:
6604             if (event->state & GDK_CONTROL_MASK) {
6605                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6606                 return TRUE; // I consumed the event
6607             }
6608             break;
6609     }
6610     return FALSE;
6614 void
6615 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6616                                GObject     *tbl)
6618      // quit if run by the _changed callbacks
6619     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6620         return;
6621     }
6623     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6625    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6627     // If this is not from selecting a size in the list (in which case get_active will give the
6628     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6629     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6630     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6631    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6632         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6633         return;
6634    }
6636     gdouble value = -1;
6637     {
6638         gchar *endptr;
6639         gchar *const text = gtk_combo_box_get_active_text(cbox);
6640         if (text) {
6641             value = g_strtod(text, &endptr);
6642             if (endptr == text) {  // Conversion failed, non-numeric input.
6643                 value = -1;
6644             }
6645             g_free(text);
6646         }
6647     }
6648     if (value <= 0) {
6649         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6650         return; // could not parse value
6651     }
6653     SPCSSAttr *css = sp_repr_css_attr_new ();
6654     Inkscape::CSSOStringStream osfs;
6655     osfs << value;
6656     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6658     SPStyle *query =
6659         sp_style_new (SP_ACTIVE_DOCUMENT);
6660     int result_numbers =
6661         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6663     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6664     if (result_numbers == QUERY_STYLE_NOTHING)
6665     {
6666         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6667         prefs->mergeStyle("/tools/text/style", css);
6668     }
6670     sp_style_unref(query);
6672     sp_desktop_set_style (desktop, css, true, true);
6673     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6674                                    _("Text: Change font size"));
6675     sp_repr_css_attr_unref (css);
6677     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6679     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6682 gboolean
6683 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6685     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6686     if (!desktop) return FALSE;
6688     if (!g_object_get_data (tbl, "esc-pressed")) {
6689         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6690         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6691         sp_text_toolbox_size_changed (cbox, tbl);
6692         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6693     }
6694     return FALSE; // I consumed the event
6698 gboolean
6699 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6701     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6702     if (!desktop) return FALSE;
6704     switch (get_group0_keyval (event)) {
6705         case GDK_Escape: // defocus
6706             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6707             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6708             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6709             return TRUE; // I consumed the event
6710             break;
6711         case GDK_Return: // defocus
6712         case GDK_KP_Enter:
6713             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6714             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6715             sp_text_toolbox_size_changed (cbox, tbl);
6716             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6717             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6718             return TRUE; // I consumed the event
6719             break;
6720     }
6721     return FALSE;
6724 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6725 // does not work!
6726 gboolean
6727 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6728                                  GdkEventFocus    */*event*/,
6729                                  GObject          *tbl)
6731     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6732     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6733     return FALSE;
6736 gboolean
6737 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6738                                  GdkEventFocus    */*event*/,
6739                                  GObject          *tbl)
6741     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6742     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6743     return FALSE;
6746 void
6747 cell_data_func  (GtkCellLayout */*cell_layout*/,
6748                  GtkCellRenderer   *cell,
6749                  GtkTreeModel      *tree_model,
6750                  GtkTreeIter       *iter,
6751                  gpointer           /*data*/)
6753     gchar *family;
6754     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6755     gchar *const family_escaped = g_markup_escape_text(family, -1);
6757     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6758     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6759     if (show_sample) {
6761         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6762         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6764     std::stringstream markup;
6765     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6766            << family_escaped << "'>" << sample_escaped << "</span>";
6767     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6769         g_free(sample_escaped);
6770     } else {
6771         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6772     }
6774     g_free(family);
6775     g_free(family_escaped);
6778 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6779                                                 GtkTreeModel       *model,
6780                                                 GtkTreeIter        *iter,
6781                                                 GObject            *tbl)
6783     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6784     // press Enter again after choosing a completion)
6785     gchar *family = 0;
6786     gtk_tree_model_get(model, iter, 0, &family, -1);
6788     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6789     gtk_entry_set_text (GTK_ENTRY (entry), family);
6791     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6792     sp_text_toolbox_family_changed (NULL, tbl);
6793     return TRUE;
6797 static void
6798 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6799     GtkEntry *entry;
6800     GtkEntryCompletion *completion;
6801     GtkTreeModel *model;
6803     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6804     completion = gtk_entry_completion_new();
6805     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6806     gtk_entry_completion_set_model(completion, model);
6807     gtk_entry_completion_set_text_column(completion, 0);
6808     gtk_entry_completion_set_inline_completion(completion, FALSE);
6809     gtk_entry_completion_set_inline_selection(completion, FALSE);
6810     gtk_entry_completion_set_popup_completion(completion, TRUE);
6811     gtk_entry_set_completion(entry, completion);
6813     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6815     g_object_unref(completion);
6818 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6819                                       void */*property*/,
6820                                       GObject *tbl)
6822   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6824   gboolean shown;
6825   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6826   if (shown) {
6827          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6828          //g_print("POP: notify: SHOWN\n");
6829   } else {
6830          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6832          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6833          // find out if the drop down was closed by Enter and if so, manually update (only
6834          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6835          GdkEvent *ev = gtk_get_current_event();
6836          if (ev) {
6837              //g_print ("ev type: %d\n", ev->type);
6838              if (ev->type == GDK_KEY_PRESS) {
6839                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6840                      case GDK_KP_Enter: // chosen
6841                      case GDK_Return:
6842                      {
6843                          // make sure the chosen one is inserted into the entry
6844                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6845                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6846                          GtkTreeIter iter;
6847                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6848                          if (has_active) {
6849                              gchar *family;
6850                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6851                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6852                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6853                          }
6855                          // update
6856                          sp_text_toolbox_family_changed (NULL, tbl);
6857                          break;
6858                      }
6859                  }
6860              }
6861          }
6863          // regardless of whether we updated, defocus the widget
6864          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6865          if (desktop)
6866              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6867          //g_print("POP: notify: HIDDEN\n");
6868   }
6871 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6873     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6874     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6876     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6877     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6879     GtkTooltips *tt = gtk_tooltips_new();
6881     ////////////Family
6882     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6883     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6885     gtk_rc_parse_string (
6886        "style \"dropdown-as-list-style\"\n"
6887        "{\n"
6888        "    GtkComboBox::appears-as-list = 1\n"
6889        "}\n"
6890        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6891     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6892     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6894     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6896     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6898     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6899     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6901     // expand the field a bit so as to view more of the previews in the drop-down
6902     GtkRequisition req;
6903     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6904     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6906     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6907     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6909     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6910     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6911              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6912     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6913     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6914     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6916     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6917     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6919     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6920     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6921     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6922     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6924     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6925     GtkWidget *box = gtk_event_box_new ();
6926     gtk_container_add (GTK_CONTAINER (box), image);
6927     gtk_toolbar_append_widget( tbl, box, "", "");
6928     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6929     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6930     gtk_widget_hide (GTK_WIDGET (box));
6931     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6933     ////////////Size
6934     gchar const *const sizes[] = {
6935         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6936         "16", "18", "20", "22", "24", "28",
6937         "32", "36", "40", "48", "56", "64", "72", "144"
6938     };
6940     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6941     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6942         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6943     }
6944     gtk_widget_set_size_request (cbox, 80, -1);
6945     gtk_toolbar_append_widget( tbl, cbox, "", "");
6946     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6947     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6948     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6949     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6951     ////////////Text anchor
6952     GtkWidget *group   = gtk_radio_button_new (NULL);
6953     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6954     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6956     // left
6957     GtkWidget *rbutton = group;
6958     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6959     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6960     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6962     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6963     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6964     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6965     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6967     // center
6968     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6969     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6970     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6971     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6973     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6974     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6975     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6976     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6978     // right
6979     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6980     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6981     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6982     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6984     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6985     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6986     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6987     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6989     // fill
6990     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6991     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6992     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6993     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6995     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6996     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6997     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6998     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7000     gtk_toolbar_append_widget( tbl, row, "", "");
7002     //spacer
7003     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7005     ////////////Text style
7006     row = gtk_hbox_new (FALSE, 4);
7008     // bold
7009     rbutton = gtk_toggle_button_new ();
7010     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7011     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7012     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7013     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7015     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7016     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7017     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7019     // italic
7020     rbutton = gtk_toggle_button_new ();
7021     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7022     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7023     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7024     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7026     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7027     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7028     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7030     gtk_toolbar_append_widget( tbl, row, "", "");
7032     //spacer
7033     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7035     // Text orientation
7036     group   = gtk_radio_button_new (NULL);
7037     row     = gtk_hbox_new (FALSE, 4);
7038     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7040     // horizontal
7041     rbutton = group;
7042     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7043     gtk_container_add           (GTK_CONTAINER (rbutton),
7044                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7045     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7046     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7048     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7049     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7050     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7052     // vertical
7053     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7054     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7055     gtk_container_add           (GTK_CONTAINER (rbutton),
7056                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7057     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7058     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7060     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7061     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7062     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7063     gtk_toolbar_append_widget( tbl, row, "", "" );
7066     //watch selection
7067     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7069     sigc::connection *c_selection_changed =
7070         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7071                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7072     pool->add_connection ("selection-changed", c_selection_changed);
7074     sigc::connection *c_selection_modified =
7075         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7076                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7077     pool->add_connection ("selection-modified", c_selection_modified);
7079     sigc::connection *c_subselection_changed =
7080         new sigc::connection (desktop->connectToolSubselectionChanged
7081                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7082     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7084     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7087     gtk_widget_show_all( GTK_WIDGET(tbl) );
7089     return GTK_WIDGET(tbl);
7090 } // end of sp_text_toolbox_new()
7092 }//<unnamed> namespace
7095 //#########################
7096 //##      Connector      ##
7097 //#########################
7099 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7101     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7102     prefs->setBool("/tools/connector/mode",
7103                 gtk_toggle_action_get_active( act ));
7106 static void sp_connector_path_set_avoid(void)
7108     cc_selection_set_avoid(true);
7112 static void sp_connector_path_set_ignore(void)
7114     cc_selection_set_avoid(false);
7117 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7119     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7120     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7121     SPDocument *doc = sp_desktop_document(desktop);
7123     if (!sp_document_get_undo_sensitive(doc))
7124     {
7125         return;
7126     }
7129     // quit if run by the _changed callbacks
7130     if (g_object_get_data( tbl, "freeze" )) {
7131         return;
7132     }
7134     // in turn, prevent callbacks from responding
7135     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7137     bool is_orthog = gtk_toggle_action_get_active( act );
7138     gchar orthog_str[] = "orthogonal";
7139     gchar polyline_str[] = "polyline";
7140     gchar *value = is_orthog ? orthog_str : polyline_str ;
7142     bool modmade = false;
7143     GSList *l = (GSList *) selection->itemList();
7144     while (l) {
7145         SPItem *item = (SPItem *) l->data;
7147         if (cc_item_is_connector(item)) {
7148             sp_object_setAttribute(item, "inkscape:connector-type",
7149                     value, false);
7150             item->avoidRef->handleSettingChange();
7151             modmade = true;
7152         }
7153         l = l->next;
7154     }
7156     if (!modmade)
7157     {
7158         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7159         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7160     }
7162     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7163             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7165     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7168 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7170     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7171     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7172     SPDocument *doc = sp_desktop_document(desktop);
7174     if (!sp_document_get_undo_sensitive(doc))
7175     {
7176         return;
7177     }
7180     // quit if run by the _changed callbacks
7181     if (g_object_get_data( tbl, "freeze" )) {
7182         return;
7183     }
7185     // in turn, prevent callbacks from responding
7186     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7188     gdouble newValue = gtk_adjustment_get_value(adj);
7189     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7190     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7192     bool modmade = false;
7193     GSList *l = (GSList *) selection->itemList();
7194     while (l) {
7195         SPItem *item = (SPItem *) l->data;
7197         if (cc_item_is_connector(item)) {
7198             sp_object_setAttribute(item, "inkscape:connector-curvature",
7199                     value, false);
7200             item->avoidRef->handleSettingChange();
7201             modmade = true;
7202         }
7203         l = l->next;
7204     }
7206     if (!modmade)
7207     {
7208         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7209         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7210     }
7212     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7213             _("Change connector curvature"));
7215     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7219 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7221     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7222     SPDocument *doc = sp_desktop_document(desktop);
7224     if (!sp_document_get_undo_sensitive(doc))
7225     {
7226         return;
7227     }
7229     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7231     if ( !repr->attribute("inkscape:connector-spacing") &&
7232             ( adj->value == defaultConnSpacing )) {
7233         // Don't need to update the repr if the attribute doesn't
7234         // exist and it is being set to the default value -- as will
7235         // happen at startup.
7236         return;
7237     }
7239     // quit if run by the attr_changed listener
7240     if (g_object_get_data( tbl, "freeze" )) {
7241         return;
7242     }
7244     // in turn, prevent listener from responding
7245     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7247     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7248     SP_OBJECT(desktop->namedview)->updateRepr();
7250     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7251     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7252         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7253         Geom::Matrix m = Geom::identity();
7254         avoid_item_move(&m, item);
7255     }
7257     if (items) {
7258         g_slist_free(items);
7259     }
7261     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7262             _("Change connector spacing"));
7264     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7267 static void sp_connector_graph_layout(void)
7269     if (!SP_ACTIVE_DESKTOP) return;
7270     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7272     // hack for clones, see comment in align-and-distribute.cpp
7273     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7274     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7276     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7278     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7280     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7283 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7285     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7286     prefs->setBool("/tools/connector/directedlayout",
7287                 gtk_toggle_action_get_active( act ));
7290 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7292     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7293     prefs->setBool("/tools/connector/avoidoverlaplayout",
7294                 gtk_toggle_action_get_active( act ));
7298 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7300     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7301     prefs->setDouble("/tools/connector/length", adj->value);
7304 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7305                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7306                                             bool /*is_interactive*/, gpointer data)
7308     GtkWidget *tbl = GTK_WIDGET(data);
7310     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7311         return;
7312     }
7313     if (strcmp(name, "inkscape:connector-spacing") == 0)
7314     {
7315         GtkAdjustment *adj = (GtkAdjustment*)
7316                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7317         gdouble spacing = defaultConnSpacing;
7318         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7320         gtk_adjustment_set_value(adj, spacing);
7321         gtk_adjustment_value_changed(adj);
7322     }
7324     spinbutton_defocus(GTK_OBJECT(tbl));
7327 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7329     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7330     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7332     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7333         cc_create_connection_point(cc);
7336 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7338     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7339     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7341     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7342         cc_remove_connection_point(cc);
7345 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7346     NULL, /* child_added */
7347     NULL, /* child_removed */
7348     connector_tb_event_attr_changed,
7349     NULL, /* content_changed */
7350     NULL  /* order_changed */
7351 };
7353 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7355     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7356     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7357     SPItem *item = selection->singleItem();
7358     if (SP_IS_PATH(item))
7359     {
7360         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7361         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7362         gtk_toggle_action_set_active(act, is_orthog);
7363         gtk_adjustment_set_value(adj, curvature);
7364     }
7368 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7370     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7371     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7373     // Editing mode toggle button
7374     {
7375         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7376                                                       _("EditMode"),
7377                                                       _("Switch between connection point editing and connector drawing mode"),
7378                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7379                                                       Inkscape::ICON_SIZE_DECORATION );
7380         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7382         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7383         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7384         g_object_set_data( holder, "mode", act );
7385         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7386     }
7389     {
7390         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7391                                           _("Avoid"),
7392                                           _("Make connectors avoid selected objects"),
7393                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7394                                           secondarySize );
7395         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7396         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7397     }
7399     {
7400         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7401                                           _("Ignore"),
7402                                           _("Make connectors ignore selected objects"),
7403                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7404                                           secondarySize );
7405         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7406         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7407     }
7409     // Orthogonal connectors toggle button
7410     {
7411         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7412                                                       _("Orthogonal"),
7413                                                       _("Make connector orthogonal or polyline"),
7414                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7415                                                       Inkscape::ICON_SIZE_DECORATION );
7416         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7418         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7419         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7420         g_object_set_data( holder, "orthogonal", act );
7421         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7422     }
7424     EgeAdjustmentAction* eact = 0;
7425     // Curvature spinbox
7426     eact = create_adjustment_action( "ConnectorCurvatureAction",
7427                                     _("Connector Curvature"), _("Curvature:"),
7428                                     _("The amount of connectors curvature"),
7429                                     "/tools/connector/curvature", defaultConnCurvature,
7430                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7431                                     0, 100, 1.0, 10.0,
7432                                     0, 0, 0,
7433                                     connector_curvature_changed, 1, 0 );
7434     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7436     // Spacing spinbox
7437     eact = create_adjustment_action( "ConnectorSpacingAction",
7438                                     _("Connector Spacing"), _("Spacing:"),
7439                                     _("The amount of space left around objects by auto-routing connectors"),
7440                                     "/tools/connector/spacing", defaultConnSpacing,
7441                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7442                                     0, 100, 1.0, 10.0,
7443                                     0, 0, 0,
7444                                     connector_spacing_changed, 1, 0 );
7445     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7447     // Graph (connector network) layout
7448     {
7449         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7450                                           _("Graph"),
7451                                           _("Nicely arrange selected connector network"),
7452                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7453                                           secondarySize );
7454         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7455         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7456     }
7458     // Default connector length spinbox
7459     eact = create_adjustment_action( "ConnectorLengthAction",
7460                                      _("Connector Length"), _("Length:"),
7461                                      _("Ideal length for connectors when layout is applied"),
7462                                      "/tools/connector/length", 100,
7463                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7464                                      10, 1000, 10.0, 100.0,
7465                                      0, 0, 0,
7466                                      connector_length_changed, 1, 0 );
7467     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7470     // Directed edges toggle button
7471     {
7472         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7473                                                       _("Downwards"),
7474                                                       _("Make connectors with end-markers (arrows) point downwards"),
7475                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7476                                                       Inkscape::ICON_SIZE_DECORATION );
7477         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7479         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7480         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7482         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7483         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7484         );
7485     }
7487     // Avoid overlaps toggle button
7488     {
7489         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7490                                                       _("Remove overlaps"),
7491                                                       _("Do not allow overlapping shapes"),
7492                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7493                                                       Inkscape::ICON_SIZE_DECORATION );
7494         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7496         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7497         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7499         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7500     }
7503     // New connection point button
7504     {
7505         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7506                                           _("New connection point"),
7507                                           _("Add a new connection point to the currently selected item"),
7508                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7509                                           secondarySize );
7510         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7511         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7512     }
7514     // Remove selected connection point button
7516     {
7517         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7518                                           _("Remove connection point"),
7519                                           _("Remove the currently selected connection point"),
7520                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7521                                           secondarySize );
7522         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7523         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7524     }
7527     // Code to watch for changes to the connector-spacing attribute in
7528     // the XML.
7529     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7530     g_assert(repr != NULL);
7532     purge_repr_listener( holder, holder );
7534     if (repr) {
7535         g_object_set_data( holder, "repr", repr );
7536         Inkscape::GC::anchor(repr);
7537         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7538         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7539     }
7540 } // end of sp_connector_toolbox_prep()
7543 //#########################
7544 //##     Paintbucket     ##
7545 //#########################
7547 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7549     gint channels = ege_select_one_action_get_active( act );
7550     flood_channels_set_channels( channels );
7553 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7555     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7556     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7559 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7561     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7562     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7565 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7567     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7568     SPUnit const *unit = tracker->getActiveUnit();
7569     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7571     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7572     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7575 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7577     // FIXME: make defaults settable via Inkscape Options
7578     struct KeyValue {
7579         char const *key;
7580         double value;
7581     } const key_values[] = {
7582         {"threshold", 15},
7583         {"offset", 0.0}
7584     };
7586     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7587         KeyValue const &kv = key_values[i];
7588         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7589         if ( adj ) {
7590             gtk_adjustment_set_value(adj, kv.value);
7591         }
7592     }
7594     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7595     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7596     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7597     ege_select_one_action_set_active( autogap_action, 0 );
7600 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7602     EgeAdjustmentAction* eact = 0;
7603     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7605     {
7606         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7608         GList* items = 0;
7609         gint count = 0;
7610         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7611         {
7612             GtkTreeIter iter;
7613             gtk_list_store_append( model, &iter );
7614             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7615             count++;
7616         }
7617         g_list_free( items );
7618         items = 0;
7619         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7620         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7621         ege_select_one_action_set_appearance( act1, "compact" );
7622         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7623         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7624         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7625         g_object_set_data( holder, "channels_action", act1 );
7626     }
7628     // Spacing spinbox
7629     {
7630         eact = create_adjustment_action(
7631             "ThresholdAction",
7632             _("Fill Threshold"), _("Threshold:"),
7633             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7634             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7635             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7636             0, 0, 0,
7637             paintbucket_threshold_changed, 1, 0 );
7639         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7640         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7641     }
7643     // Create the units menu.
7644     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7645     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7646     if (!stored_unit.empty())
7647         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7648     g_object_set_data( holder, "tracker", tracker );
7649     {
7650         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7651         gtk_action_group_add_action( mainActions, act );
7652     }
7654     // Offset spinbox
7655     {
7656         eact = create_adjustment_action(
7657             "OffsetAction",
7658             _("Grow/shrink by"), _("Grow/shrink by:"),
7659             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7660             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7661             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7662             0, 0, 0,
7663             paintbucket_offset_changed, 1, 2);
7664         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7666         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7667     }
7669     /* Auto Gap */
7670     {
7671         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7673         GList* items = 0;
7674         gint count = 0;
7675         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7676         {
7677             GtkTreeIter iter;
7678             gtk_list_store_append( model, &iter );
7679             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7680             count++;
7681         }
7682         g_list_free( items );
7683         items = 0;
7684         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7685         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7686         ege_select_one_action_set_appearance( act2, "compact" );
7687         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7688         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7689         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7690         g_object_set_data( holder, "autogap_action", act2 );
7691     }
7693     /* Reset */
7694     {
7695         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7696                                           _("Defaults"),
7697                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7698                                           GTK_STOCK_CLEAR );
7699         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7700         gtk_action_group_add_action( mainActions, act );
7701         gtk_action_set_sensitive( act, TRUE );
7702     }
7706 /*
7707   Local Variables:
7708   mode:c++
7709   c-file-style:"stroustrup"
7710   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7711   indent-tabs-mode:nil
7712   fill-column:99
7713   End:
7714 */
7715 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :