Code

44ad5a6ce013b6a9aef7327b2d23b54087b01da1
[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, sigc::connection*);
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     return toolboxNewCommon( tb, GTK_POS_LEFT );
877 //####################################
878 //# Commands Bar
879 //####################################
881 GtkWidget *sp_commands_toolbox_new()
883     GtkWidget *tb = gtk_toolbar_new();
885     return toolboxNewCommon( tb, GTK_POS_LEFT );
888 GtkWidget *sp_snap_toolbox_new()
890     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
892     return toolboxNewCommon( tb, GTK_POS_LEFT );
895 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
896                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
897                                                        Glib::ustring const &path, gdouble def,
898                                                        GtkWidget *focusTarget,
899                                                        GtkWidget *us,
900                                                        GObject *dataKludge,
901                                                        gboolean altx, gchar const *altx_mark,
902                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
903                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
904                                                        void (*callback)(GtkAdjustment *, GObject *),
905                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
907     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
908     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
909                                                              lower, upper, step, page, 0 ) );
910     if (us) {
911         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
912     }
914     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
916     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
917     if ( shortLabel ) {
918         g_object_set( act, "short_label", shortLabel, NULL );
919     }
921     if ( (descrCount > 0) && descrLabels && descrValues ) {
922         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
923     }
925     if ( focusTarget ) {
926         ege_adjustment_action_set_focuswidget( act, focusTarget );
927     }
929     if ( altx && altx_mark ) {
930         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
931     }
933     if ( dataKludge ) {
934         // Rather lame, but it's the only place where we need to get the entry name
935         // but we don't have an Entry
936         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
937     }
939     // Using a cast just to make sure we pass in the right kind of function pointer
940     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
942     return act;
946 //####################################
947 //# node editing callbacks
948 //####################################
950 /**
951  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
952  */
953 static ShapeEditor *get_current_shape_editor()
955     if (!SP_ACTIVE_DESKTOP) {
956         return NULL;
957     }
959     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
961     if (!SP_IS_NODE_CONTEXT(event_context)) {
962         return NULL;
963     }
965     return event_context->shape_editor;
969 void
970 sp_node_path_edit_add(void)
972     ShapeEditor *shape_editor = get_current_shape_editor();
973     if (shape_editor) shape_editor->add_node();
976 void
977 sp_node_path_edit_delete(void)
979     ShapeEditor *shape_editor = get_current_shape_editor();
980     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
983 void
984 sp_node_path_edit_delete_segment(void)
986     ShapeEditor *shape_editor = get_current_shape_editor();
987     if (shape_editor) shape_editor->delete_segment();
990 void
991 sp_node_path_edit_break(void)
993     ShapeEditor *shape_editor = get_current_shape_editor();
994     if (shape_editor) shape_editor->break_at_nodes();
997 void
998 sp_node_path_edit_join(void)
1000     ShapeEditor *shape_editor = get_current_shape_editor();
1001     if (shape_editor) shape_editor->join_nodes();
1004 void
1005 sp_node_path_edit_join_segment(void)
1007     ShapeEditor *shape_editor = get_current_shape_editor();
1008     if (shape_editor) shape_editor->join_segments();
1011 void
1012 sp_node_path_edit_toline(void)
1014     ShapeEditor *shape_editor = get_current_shape_editor();
1015     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1018 void
1019 sp_node_path_edit_tocurve(void)
1021     ShapeEditor *shape_editor = get_current_shape_editor();
1022     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1025 void
1026 sp_node_path_edit_cusp(void)
1028     ShapeEditor *shape_editor = get_current_shape_editor();
1029     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1032 void
1033 sp_node_path_edit_smooth(void)
1035     ShapeEditor *shape_editor = get_current_shape_editor();
1036     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1039 void
1040 sp_node_path_edit_symmetrical(void)
1042     ShapeEditor *shape_editor = get_current_shape_editor();
1043     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1046 void
1047 sp_node_path_edit_auto(void)
1049     ShapeEditor *shape_editor = get_current_shape_editor();
1050     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1053 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1054     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1055     bool show = gtk_toggle_action_get_active( act );
1056     prefs->setBool("/tools/nodes/show_handles",  show);
1057     ShapeEditor *shape_editor = get_current_shape_editor();
1058     if (shape_editor) shape_editor->show_handles(show);
1061 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1062     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1063     bool show = gtk_toggle_action_get_active( act );
1064     prefs->setBool("/tools/nodes/show_helperpath",  show);
1065     ShapeEditor *shape_editor = get_current_shape_editor();
1066     if (shape_editor) shape_editor->show_helperpath(show);
1069 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1070     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1073 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1074     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1077 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1078     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1081 /* is called when the node selection is modified */
1082 static void
1083 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1085     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1086     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1087     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1088     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1090     // quit if run by the attr_changed listener
1091     if (g_object_get_data( tbl, "freeze" )) {
1092         return;
1093     }
1095     // in turn, prevent listener from responding
1096     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1098     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1099     SPUnit const *unit = tracker->getActiveUnit();
1101     ShapeEditor *shape_editor = get_current_shape_editor();
1102     if (shape_editor && shape_editor->has_nodepath()) {
1103         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1104         int n_selected = 0;
1105         if (nodepath) {
1106             n_selected = nodepath->numSelected();
1107         }
1109         if (n_selected == 0) {
1110             gtk_action_set_sensitive(xact, FALSE);
1111             gtk_action_set_sensitive(yact, FALSE);
1112         } else {
1113             gtk_action_set_sensitive(xact, TRUE);
1114             gtk_action_set_sensitive(yact, TRUE);
1115             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1116             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1118             if (n_selected == 1) {
1119                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1120                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1121                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1122                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1123                 }
1124             } else {
1125                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1126                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1127                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1128                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1129                        selected nodes don't coincide (in this case we use the coordinates of the center
1130                        of the bounding box). So the entries are never set to zero. */
1131                     // FIXME: Maybe we should clear the entry if several nodes are selected
1132                     //        instead of providing a kind of average value
1133                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1134                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1135                 }
1136             }
1137         }
1138     } else {
1139         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1140         gtk_action_set_sensitive(xact, FALSE);
1141         gtk_action_set_sensitive(yact, FALSE);
1142     }
1144     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1147 static void
1148 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1150     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1151     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1153     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1154     SPUnit const *unit = tracker->getActiveUnit();
1156     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1157         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1158     }
1160     // quit if run by the attr_changed listener
1161     if (g_object_get_data( tbl, "freeze" )) {
1162         return;
1163     }
1165     // in turn, prevent listener from responding
1166     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1168     ShapeEditor *shape_editor = get_current_shape_editor();
1169     if (shape_editor && shape_editor->has_nodepath()) {
1170         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1171         if (!strcmp(value_name, "x")) {
1172             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1173         }
1174         if (!strcmp(value_name, "y")) {
1175             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1176         }
1177     }
1179     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1182 static void
1183 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1185     sp_node_path_value_changed(adj, tbl, "x");
1188 static void
1189 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1191     sp_node_path_value_changed(adj, tbl, "y");
1194 void
1195 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1197     {
1198     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1199     SPItem *item = selection->singleItem();
1200     if (item && SP_IS_LPE_ITEM(item)) {
1201        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1202            gtk_action_set_sensitive(w, TRUE);
1203        } else {
1204            gtk_action_set_sensitive(w, FALSE);
1205        }
1206     } else {
1207        gtk_action_set_sensitive(w, FALSE);
1208     }
1209     }
1211     {
1212     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1213     SPItem *item = selection->singleItem();
1214     if (item && item->clip_ref && item->clip_ref->getObject()) {
1215        gtk_action_set_sensitive(w, TRUE);
1216     } else {
1217        gtk_action_set_sensitive(w, FALSE);
1218     }
1219     }
1221     {
1222     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1223     SPItem *item = selection->singleItem();
1224     if (item && item->mask_ref && item->mask_ref->getObject()) {
1225        gtk_action_set_sensitive(w, TRUE);
1226     } else {
1227        gtk_action_set_sensitive(w, FALSE);
1228     }
1229     }
1232 void
1233 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1235     sp_node_toolbox_sel_changed (selection, tbl);
1240 //################################
1241 //##    Node Editing Toolbox    ##
1242 //################################
1244 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1246     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1247     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1248     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1249     g_object_set_data( holder, "tracker", tracker );
1251     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1253     {
1254         InkAction* inky = ink_action_new( "NodeInsertAction",
1255                                           _("Insert node"),
1256                                           _("Insert new nodes into selected segments"),
1257                                           INKSCAPE_ICON_NODE_ADD,
1258                                           secondarySize );
1259         g_object_set( inky, "short_label", _("Insert"), NULL );
1260         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1261         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1262     }
1264     {
1265         InkAction* inky = ink_action_new( "NodeDeleteAction",
1266                                           _("Delete node"),
1267                                           _("Delete selected nodes"),
1268                                           INKSCAPE_ICON_NODE_DELETE,
1269                                           secondarySize );
1270         g_object_set( inky, "short_label", _("Delete"), NULL );
1271         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1272         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273     }
1275     {
1276         InkAction* inky = ink_action_new( "NodeJoinAction",
1277                                           _("Join endnodes"),
1278                                           _("Join selected endnodes"),
1279                                           INKSCAPE_ICON_NODE_JOIN,
1280                                           secondarySize );
1281         g_object_set( inky, "short_label", _("Join"), NULL );
1282         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1283         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284     }
1286     {
1287         InkAction* inky = ink_action_new( "NodeBreakAction",
1288                                           _("Break nodes"),
1289                                           _("Break path at selected nodes"),
1290                                           INKSCAPE_ICON_NODE_BREAK,
1291                                           secondarySize );
1292         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1293         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294     }
1297     {
1298         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1299                                           _("Join with segment"),
1300                                           _("Join selected endnodes with a new segment"),
1301                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1302                                           secondarySize );
1303         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1304         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1305     }
1307     {
1308         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1309                                           _("Delete segment"),
1310                                           _("Delete segment between two non-endpoint nodes"),
1311                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1312                                           secondarySize );
1313         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1314         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315     }
1317     {
1318         InkAction* inky = ink_action_new( "NodeCuspAction",
1319                                           _("Node Cusp"),
1320                                           _("Make selected nodes corner"),
1321                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1322                                           secondarySize );
1323         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1324         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1325     }
1327     {
1328         InkAction* inky = ink_action_new( "NodeSmoothAction",
1329                                           _("Node Smooth"),
1330                                           _("Make selected nodes smooth"),
1331                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1332                                           secondarySize );
1333         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1334         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1335     }
1337     {
1338         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1339                                           _("Node Symmetric"),
1340                                           _("Make selected nodes symmetric"),
1341                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1342                                           secondarySize );
1343         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1344         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1345     }
1347     {
1348         InkAction* inky = ink_action_new( "NodeAutoAction",
1349                                           _("Node Auto"),
1350                                           _("Make selected nodes auto-smooth"),
1351                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1352                                           secondarySize );
1353         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1354         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1355     }
1357     {
1358         InkAction* inky = ink_action_new( "NodeLineAction",
1359                                           _("Node Line"),
1360                                           _("Make selected segments lines"),
1361                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1362                                           secondarySize );
1363         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1364         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365     }
1367     {
1368         InkAction* inky = ink_action_new( "NodeCurveAction",
1369                                           _("Node Curve"),
1370                                           _("Make selected segments curves"),
1371                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1372                                           secondarySize );
1373         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1374         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375     }
1377     {
1378         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1379                                                       _("Show Handles"),
1380                                                       _("Show the Bezier handles of selected nodes"),
1381                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1382                                                       Inkscape::ICON_SIZE_DECORATION );
1383         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1384         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1385         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1386     }
1388     {
1389         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1390                                                       _("Show Outline"),
1391                                                       _("Show the outline of the path"),
1392                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1393                                                       Inkscape::ICON_SIZE_DECORATION );
1394         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1395         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1396         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1397     }
1399     {
1400         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1401                                           _("Next path effect parameter"),
1402                                           _("Show next path effect parameter for editing"),
1403                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1404                                           Inkscape::ICON_SIZE_DECORATION );
1405         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1406         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1407         g_object_set_data( holder, "nodes_lpeedit", inky);
1408     }
1410     {
1411         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1412                                           _("Edit clipping path"),
1413                                           _("Edit the clipping path of the object"),
1414                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1415                                           Inkscape::ICON_SIZE_DECORATION );
1416         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418         g_object_set_data( holder, "nodes_clippathedit", inky);
1419     }
1421     {
1422         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1423                                           _("Edit mask path"),
1424                                           _("Edit the mask of the object"),
1425                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1426                                           Inkscape::ICON_SIZE_DECORATION );
1427         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429         g_object_set_data( holder, "nodes_maskedit", inky);
1430     }
1432     /* X coord of selected node(s) */
1433     {
1434         EgeAdjustmentAction* eact = 0;
1435         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1436         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1437         eact = create_adjustment_action( "NodeXAction",
1438                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1439                                          "/tools/nodes/Xcoord", 0,
1440                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1441                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1442                                          labels, values, G_N_ELEMENTS(labels),
1443                                          sp_node_path_x_value_changed );
1444         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1445         g_object_set_data( holder, "nodes_x_action", eact );
1446         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1447         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1448     }
1450     /* Y coord of selected node(s) */
1451     {
1452         EgeAdjustmentAction* eact = 0;
1453         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1454         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1455         eact = create_adjustment_action( "NodeYAction",
1456                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1457                                          "/tools/nodes/Ycoord", 0,
1458                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1459                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1460                                          labels, values, G_N_ELEMENTS(labels),
1461                                          sp_node_path_y_value_changed );
1462         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1463         g_object_set_data( holder, "nodes_y_action", eact );
1464         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1465         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1466     }
1468     // add the units menu
1469     {
1470         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1471         gtk_action_group_add_action( mainActions, act );
1472     }
1475     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1477     //watch selection
1478     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1480     sigc::connection *c_selection_changed =
1481         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1482                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1483     pool->add_connection ("selection-changed", c_selection_changed);
1485     sigc::connection *c_selection_modified =
1486         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1487                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1488     pool->add_connection ("selection-modified", c_selection_modified);
1490     sigc::connection *c_subselection_changed =
1491         new sigc::connection (desktop->connectToolSubselectionChanged
1492                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1493     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1495     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1497     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1498 } // end of sp_node_toolbox_prep()
1501 //########################
1502 //##    Zoom Toolbox    ##
1503 //########################
1505 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1507     // no custom GtkAction setup needed
1508 } // end of sp_zoom_toolbox_prep()
1510 void
1511 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1513     toolbox_set_desktop(toolbox,
1514                         desktop,
1515                         setup_tool_toolbox,
1516                         update_tool_toolbox,
1517                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1518                                                                          "event_context_connection")));
1522 void
1523 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1525     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1526                         desktop,
1527                         setup_aux_toolbox,
1528                         update_aux_toolbox,
1529                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1530                                                                          "event_context_connection")));
1533 void
1534 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1536     toolbox_set_desktop(toolbox,
1537                         desktop,
1538                         setup_commands_toolbox,
1539                         update_commands_toolbox,
1540                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1541                                                                          "event_context_connection")));
1544 void
1545 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1547     toolbox_set_desktop(toolbox,
1548                         desktop,
1549                         setup_snap_toolbox,
1550                         update_snap_toolbox,
1551                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1552                                                                          "event_context_connection")));
1556 static void
1557 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1559     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1560     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1562     if (old_desktop) {
1563         GList *children, *iter;
1565         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1566         for ( iter = children ; iter ; iter = iter->next ) {
1567             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1568         }
1569         g_list_free(children);
1570     }
1572     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1574     if (desktop) {
1575         gtk_widget_set_sensitive(toolbox, TRUE);
1576         setup_func(toolbox, desktop);
1577         update_func(desktop, desktop->event_context, toolbox);
1578         *conn = desktop->connectEventContextChanged
1579             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1580     } else {
1581         gtk_widget_set_sensitive(toolbox, FALSE);
1582     }
1584 } // end of toolbox_set_desktop()
1587 static void setupToolboxCommon( GtkWidget *toolbox,
1588                                 SPDesktop *desktop,
1589                                 gchar const *descr,
1590                                 gchar const* toolbarName,
1591                                 gchar const* sizePref,
1592                                 GtkOrientation orientation )
1594     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1595     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1597     GtkUIManager* mgr = gtk_ui_manager_new();
1598     GError* errVal = 0;
1600     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1601     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1603     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1604     if ( prefs->getBool("/toolbox/icononly", true) ) {
1605         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1606     }
1608     Inkscape::IconSize toolboxSize = prefToSize(sizePref);
1609     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1611     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1612     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1614     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1616     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1617     if ( child ) {
1618         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1619     }
1621     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1624 static void
1625 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1627     gchar const * descr =
1628         "<ui>"
1629         "  <toolbar name='ToolToolbar'>"
1630         "    <toolitem action='ToolSelector' />"
1631         "    <toolitem action='ToolNode' />"
1632         "    <toolitem action='ToolTweak' />"
1633         "    <toolitem action='ToolSpray' />"
1634         "    <toolitem action='ToolZoom' />"
1635         "    <toolitem action='ToolRect' />"
1636         "    <toolitem action='Tool3DBox' />"
1637         "    <toolitem action='ToolArc' />"
1638         "    <toolitem action='ToolStar' />"
1639         "    <toolitem action='ToolSpiral' />"
1640         "    <toolitem action='ToolPencil' />"
1641         "    <toolitem action='ToolPen' />"
1642         "    <toolitem action='ToolCalligraphic' />"
1643         "    <toolitem action='ToolEraser' />"
1644 //        "    <toolitem action='ToolLPETool' />"
1645         "    <toolitem action='ToolPaintBucket' />"
1646         "    <toolitem action='ToolText' />"
1647         "    <toolitem action='ToolConnector' />"
1648         "    <toolitem action='ToolGradient' />"
1649         "    <toolitem action='ToolDropper' />"
1650         "  </toolbar>"
1651         "</ui>";
1653     setupToolboxCommon( toolbox, desktop, descr,
1654                         "/ui/ToolToolbar",
1655                         "/toolbox/tools/small",
1656                         GTK_ORIENTATION_VERTICAL );
1659 static void
1660 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1662     gchar const *const tname = ( eventcontext
1663                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1664                                  : NULL );
1665     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1667     for (int i = 0 ; tools[i].type_name ; i++ ) {
1668         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1669         if ( act ) {
1670             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1671             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1672             if ( verbAct ) {
1673                 verbAct->set_active(setActive);
1674             }
1675         }
1676     }
1679 static void
1680 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1682     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1683     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1684     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1685     GtkUIManager* mgr = gtk_ui_manager_new();
1686     GError* errVal = 0;
1687     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1688     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1690     std::map<std::string, GtkWidget*> dataHolders;
1692     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1693         if ( aux_toolboxes[i].prep_func ) {
1694             // converted to GtkActions and UIManager
1696             GtkWidget* kludge = gtk_toolbar_new();
1697             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1698             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1699             dataHolders[aux_toolboxes[i].type_name] = kludge;
1700             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1701         } else {
1703             GtkWidget *sub_toolbox = 0;
1704             if (aux_toolboxes[i].create_func == NULL) {
1705                 sub_toolbox = sp_empty_toolbox_new(desktop);
1706             } else {
1707                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1708             }
1710             gtk_size_group_add_widget( grouper, sub_toolbox );
1712             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1713             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1715         }
1716     }
1718     // Second pass to create toolbars *after* all GtkActions are created
1719     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1720         if ( aux_toolboxes[i].prep_func ) {
1721             // converted to GtkActions and UIManager
1723             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1725             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1726             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1728             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1729             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1730             g_free( tmp );
1731             tmp = 0;
1733             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1734                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1735             }
1737             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1738             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1740             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1742             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1743                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1744                 swatch->setDesktop( desktop );
1745                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1746                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1747                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1748                 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 );
1749             }
1751             gtk_widget_show_all( holder );
1752             sp_set_font_size_smaller( holder );
1754             gtk_size_group_add_widget( grouper, holder );
1756             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1757             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1758         }
1759     }
1761     g_object_unref( G_OBJECT(grouper) );
1764 static void
1765 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1767     gchar const *tname = ( eventcontext
1768                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1769                            : NULL );
1770     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1771         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1772         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1773             gtk_widget_show_all(sub_toolbox);
1774             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1775         } else {
1776             gtk_widget_hide(sub_toolbox);
1777         }
1778     }
1781 static void
1782 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1784     gchar const * descr =
1785         "<ui>"
1786         "  <toolbar name='CommandsToolbar'>"
1787         "    <toolitem action='FileNew' />"
1788         "    <toolitem action='FileOpen' />"
1789         "    <toolitem action='FileSave' />"
1790         "    <toolitem action='FilePrint' />"
1791         "    <separator />"
1792         "    <toolitem action='FileImport' />"
1793         "    <toolitem action='FileExport' />"
1794         "    <separator />"
1795         "    <toolitem action='EditUndo' />"
1796         "    <toolitem action='EditRedo' />"
1797         "    <separator />"
1798         "    <toolitem action='EditCopy' />"
1799         "    <toolitem action='EditCut' />"
1800         "    <toolitem action='EditPaste' />"
1801         "    <separator />"
1802         "    <toolitem action='ZoomSelection' />"
1803         "    <toolitem action='ZoomDrawing' />"
1804         "    <toolitem action='ZoomPage' />"
1805         "    <separator />"
1806         "    <toolitem action='EditDuplicate' />"
1807         "    <toolitem action='EditClone' />"
1808         "    <toolitem action='EditUnlinkClone' />"
1809         "    <separator />"
1810         "    <toolitem action='SelectionGroup' />"
1811         "    <toolitem action='SelectionUnGroup' />"
1812         "    <separator />"
1813         "    <toolitem action='DialogFillStroke' />"
1814         "    <toolitem action='DialogText' />"
1815         "    <toolitem action='DialogLayers' />"
1816         "    <toolitem action='DialogXMLEditor' />"
1817         "    <toolitem action='DialogAlignDistribute' />"
1818         "    <separator />"
1819         "    <toolitem action='DialogPreferences' />"
1820         "    <toolitem action='DialogDocumentProperties' />"
1821         "  </toolbar>"
1822         "</ui>";
1824     setupToolboxCommon( toolbox, desktop, descr,
1825                         "/ui/CommandsToolbar",
1826                         "/toolbox/small",
1827                         GTK_ORIENTATION_HORIZONTAL );
1830 static void
1831 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1835 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1837     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1838         return;
1839     }
1841     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1842     g_assert(ptr != NULL);
1844     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1845     SPNamedView *nv = sp_desktop_namedview(dt);
1846     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1848     if (dt == NULL || nv == NULL) {
1849         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1850         return;
1851     }
1853     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1855     if (repr == NULL) {
1856         g_warning("This namedview doesn't have a xml representation attached!");
1857         return;
1858     }
1860     bool saved = sp_document_get_undo_sensitive(doc);
1861     sp_document_set_undo_sensitive(doc, false);
1863     bool v = false;
1864     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1866     switch (attr) {
1867         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1868             dt->toggleSnapGlobal();
1869             break;
1870         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1871             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1872             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1873             break;
1874         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1875             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1876             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1877             break;
1878         case SP_ATTR_INKSCAPE_BBOX_NODES:
1879             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1880             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1881             break;
1882         case SP_ATTR_INKSCAPE_SNAP_NODES:
1883             v = nv->snap_manager.snapprefs.getSnapModeNode();
1884             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1885             break;
1886         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1887             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1888             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1889             break;
1890         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1891             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1892             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1893             break;
1894         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1895             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1896             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1897             break;
1898         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1899             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1900             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1901             break;
1902         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1903             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1904             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1905             break;
1906         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1907             v = nv->snap_manager.snapprefs.getSnapToGrids();
1908             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1909             break;
1910         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1911             v = nv->snap_manager.snapprefs.getSnapToGuides();
1912             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1913             break;
1914         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1915             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1916             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1917             break;
1918             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1919               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1920               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1921               break;*/
1922         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1923             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1924             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1925             break;
1926         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1927             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1928             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1929             break;
1930         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1931             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1932             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1933             break;
1934         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1935             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1936             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1937             break;
1938         default:
1939             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1940             break;
1941     }
1943     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1944     doc->setModifiedSinceSave();
1946     sp_document_set_undo_sensitive(doc, saved);
1949 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1951     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1953     gchar const * descr =
1954         "<ui>"
1955         "  <toolbar name='SnapToolbar'>"
1956         "    <toolitem action='ToggleSnapGlobal' />"
1957         "    <separator />"
1958         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
1959         "    <toolitem action='ToggleSnapToBBoxPath' />"
1960         "    <toolitem action='ToggleSnapToBBoxNode' />"
1961         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1962         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
1963         "    <separator />"
1964         "    <toolitem action='ToggleSnapFromNode' />"
1965         "    <toolitem action='ToggleSnapToItemPath' />"
1966         "    <toolitem action='ToggleSnapToPathIntersections' />"
1967         "    <toolitem action='ToggleSnapToItemNode' />"
1968         "    <toolitem action='ToggleSnapToSmoothNodes' />"
1969         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
1970         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
1971         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
1972         "    <separator />"
1973         "    <toolitem action='ToggleSnapToPageBorder' />"
1974         "    <toolitem action='ToggleSnapToGrids' />"
1975         "    <toolitem action='ToggleSnapToGuides' />"
1976         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
1977         "  </toolbar>"
1978         "</ui>";
1980     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1982     {
1983         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
1984                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
1985                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
1987         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1988         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
1989     }
1991     {
1992         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
1993                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
1994                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
1996         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1997         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
1998     }
2000     {
2001         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2002                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2003                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2005         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2006         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2007     }
2009     {
2010         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2011                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2012                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2014         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2015         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2016     }
2018     {
2019         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2020                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2021                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2022                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2024         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2025         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2026     }
2028     {
2029         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2030                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2031                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2033         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2034         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2035     }
2037     {
2038         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2039                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2041         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2042         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2043     }
2045     {
2046         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2047                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2048                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2050         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2051         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2052     }
2054     {
2055         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2056                                                      _("Path intersections"), _("Snap to path intersections"),
2057                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2059         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2060         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2061     }
2063     {
2064         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2065                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2066                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2068         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2069         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2070     }
2072     {
2073         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2074                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2075                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2077         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2078         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2079     }
2081     {
2082         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2083                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2084                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2086         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2087         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2088     }
2090     {
2091         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2092                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2093                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2095         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2096         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2097     }
2099     {
2100         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2101                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2102                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2104         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2105         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2106     }
2108     {
2109         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2110                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2111                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2113         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2114         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2115     }
2117     {
2118         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2119                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2120                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2122         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2123         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2124     }
2126     {
2127         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2128                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2129                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2131         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2132         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2133     }
2135     /*{
2136       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2137       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2138       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2139       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2141       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2142       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2143       }*/
2145     setupToolboxCommon( toolbox, desktop, descr,
2146                         "/ui/SnapToolbar",
2147                         "/toolbox/secondary",
2148                         GTK_ORIENTATION_HORIZONTAL );
2151 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2153     g_assert(desktop != NULL);
2154     g_assert(toolbox != NULL);
2156     SPNamedView *nv = sp_desktop_namedview(desktop);
2157     if (nv == NULL) {
2158         g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2159         return;
2160     }
2162     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2164     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2165     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2166     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2167     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2168     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2169     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2170     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2171     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2172     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2173     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2174     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2175     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2176     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2177     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2178     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2179     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2180     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2181     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2184     if (!act1) {
2185         return; // The snap actions haven't been defined yet (might be the case during startup)
2186     }
2188     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2189     // changes in our document because we're only updating the UI;
2190     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2191     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2193     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2194     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2196     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2197     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2198     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2200     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2201     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2202     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2203     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2204     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2205     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2206     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2207     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2209     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2210     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2211     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2213     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2214     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2215     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2216     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2217     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2218     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2219     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2220     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2221     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2222     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2223     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2224     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2225     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2226     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2227     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2229     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2230     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2231     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2232     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2234     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2235     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2236     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2237     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2240     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2243 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2245     gtk_widget_show(toolbox_toplevel);
2246     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2248     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2249     if (!shown_toolbox) {
2250         return;
2251     }
2252     gtk_widget_show(toolbox);
2254     gtk_widget_show_all(shown_toolbox);
2257 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2259     GtkWidget *tbl = gtk_toolbar_new();
2260     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2261     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2263     gtk_widget_show_all(tbl);
2264     sp_set_font_size_smaller (tbl);
2266     return tbl;
2269 #define MODE_LABEL_WIDTH 70
2271 //########################
2272 //##       Star         ##
2273 //########################
2275 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2277     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2279     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2280         // do not remember prefs if this call is initiated by an undo change, because undoing object
2281         // creation sets bogus values to its attributes before it is deleted
2282         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2283         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2284     }
2286     // quit if run by the attr_changed listener
2287     if (g_object_get_data( dataKludge, "freeze" )) {
2288         return;
2289     }
2291     // in turn, prevent listener from responding
2292     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2294     bool modmade = false;
2296     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2297     GSList const *items = selection->itemList();
2298     for (; items != NULL; items = items->next) {
2299         if (SP_IS_STAR((SPItem *) items->data)) {
2300             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2301             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2302             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2303                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2304                                     + M_PI / (gint)adj->value));
2305             SP_OBJECT((SPItem *) items->data)->updateRepr();
2306             modmade = true;
2307         }
2308     }
2309     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2310                                    _("Star: Change number of corners"));
2312     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2315 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2317     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2319     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2320         if (!IS_NAN(adj->value)) {
2321             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2322             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2323         }
2324     }
2326     // quit if run by the attr_changed listener
2327     if (g_object_get_data( dataKludge, "freeze" )) {
2328         return;
2329     }
2331     // in turn, prevent listener from responding
2332     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2334     bool modmade = false;
2335     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2336     GSList const *items = selection->itemList();
2337     for (; items != NULL; items = items->next) {
2338         if (SP_IS_STAR((SPItem *) items->data)) {
2339             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2341             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2342             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2343             if (r2 < r1) {
2344                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2345             } else {
2346                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2347             }
2349             SP_OBJECT((SPItem *) items->data)->updateRepr();
2350             modmade = true;
2351         }
2352     }
2354     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2355                                    _("Star: Change spoke ratio"));
2357     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2360 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2362     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2363     bool flat = ege_select_one_action_get_active( act ) == 0;
2365     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2366         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2367         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2368     }
2370     // quit if run by the attr_changed listener
2371     if (g_object_get_data( dataKludge, "freeze" )) {
2372         return;
2373     }
2375     // in turn, prevent listener from responding
2376     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2378     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2379     GSList const *items = selection->itemList();
2380     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2381     bool modmade = false;
2383     if ( prop_action ) {
2384         gtk_action_set_sensitive( prop_action, !flat );
2385     }
2387     for (; items != NULL; items = items->next) {
2388         if (SP_IS_STAR((SPItem *) items->data)) {
2389             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2390             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2391             SP_OBJECT((SPItem *) items->data)->updateRepr();
2392             modmade = true;
2393         }
2394     }
2396     if (modmade) {
2397         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2398                          flat ? _("Make polygon") : _("Make star"));
2399     }
2401     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2404 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2406     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2408     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2409         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2410         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2411     }
2413     // quit if run by the attr_changed listener
2414     if (g_object_get_data( dataKludge, "freeze" )) {
2415         return;
2416     }
2418     // in turn, prevent listener from responding
2419     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2421     bool modmade = false;
2423     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2424     GSList const *items = selection->itemList();
2425     for (; items != NULL; items = items->next) {
2426         if (SP_IS_STAR((SPItem *) items->data)) {
2427             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2428             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2429             SP_OBJECT(items->data)->updateRepr();
2430             modmade = true;
2431         }
2432     }
2433     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2434                                    _("Star: Change rounding"));
2436     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2439 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2441     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2443     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2444         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2445         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2446     }
2448     // quit if run by the attr_changed listener
2449     if (g_object_get_data( dataKludge, "freeze" )) {
2450         return;
2451     }
2453     // in turn, prevent listener from responding
2454     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2456     bool modmade = false;
2458     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2459     GSList const *items = selection->itemList();
2460     for (; items != NULL; items = items->next) {
2461         if (SP_IS_STAR((SPItem *) items->data)) {
2462             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2463             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2464             SP_OBJECT(items->data)->updateRepr();
2465             modmade = true;
2466         }
2467     }
2468     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2469                                    _("Star: Change randomization"));
2471     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2475 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2476                                        gchar const */*old_value*/, gchar const */*new_value*/,
2477                                        bool /*is_interactive*/, gpointer data)
2479     GtkWidget *tbl = GTK_WIDGET(data);
2481     // quit if run by the _changed callbacks
2482     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2483         return;
2484     }
2486     // in turn, prevent callbacks from responding
2487     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2489     GtkAdjustment *adj = 0;
2491     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2492     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2494     if (!strcmp(name, "inkscape:randomized")) {
2495         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2496         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2497     } else if (!strcmp(name, "inkscape:rounded")) {
2498         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2499         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2500     } else if (!strcmp(name, "inkscape:flatsided")) {
2501         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2502         char const *flatsides = repr->attribute("inkscape:flatsided");
2503         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2504         if ( flatsides && !strcmp(flatsides,"false") ) {
2505             ege_select_one_action_set_active( flat_action, 1 );
2506             gtk_action_set_sensitive( prop_action, TRUE );
2507         } else {
2508             ege_select_one_action_set_active( flat_action, 0 );
2509             gtk_action_set_sensitive( prop_action, FALSE );
2510         }
2511     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2512         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2513         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2514         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2515         if (r2 < r1) {
2516             gtk_adjustment_set_value(adj, r2/r1);
2517         } else {
2518             gtk_adjustment_set_value(adj, r1/r2);
2519         }
2520     } else if (!strcmp(name, "sodipodi:sides")) {
2521         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2522         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2523     }
2525     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2529 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2531     NULL, /* child_added */
2532     NULL, /* child_removed */
2533     star_tb_event_attr_changed,
2534     NULL, /* content_changed */
2535     NULL  /* order_changed */
2536 };
2539 /**
2540  *  \param selection Should not be NULL.
2541  */
2542 static void
2543 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2545     int n_selected = 0;
2546     Inkscape::XML::Node *repr = NULL;
2548     purge_repr_listener( tbl, tbl );
2550     for (GSList const *items = selection->itemList();
2551          items != NULL;
2552          items = items->next)
2553     {
2554         if (SP_IS_STAR((SPItem *) items->data)) {
2555             n_selected++;
2556             repr = SP_OBJECT_REPR((SPItem *) items->data);
2557         }
2558     }
2560     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2562     if (n_selected == 0) {
2563         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2564     } else if (n_selected == 1) {
2565         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2567         if (repr) {
2568             g_object_set_data( tbl, "repr", repr );
2569             Inkscape::GC::anchor(repr);
2570             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2571             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2572         }
2573     } else {
2574         // FIXME: implement averaging of all parameters for multiple selected stars
2575         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2576         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2577     }
2581 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2583     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2584     // callbacks to lump all the changes for all selected objects in one undo step
2586     GtkAdjustment *adj = 0;
2588     // fixme: make settable in prefs!
2589     gint mag = 5;
2590     gdouble prop = 0.5;
2591     gboolean flat = FALSE;
2592     gdouble randomized = 0;
2593     gdouble rounded = 0;
2595     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2596     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2598     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2599     gtk_action_set_sensitive( sb2, !flat );
2601     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2602     gtk_adjustment_set_value(adj, mag);
2603     gtk_adjustment_value_changed(adj);
2605     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2606     gtk_adjustment_set_value(adj, prop);
2607     gtk_adjustment_value_changed(adj);
2609     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2610     gtk_adjustment_set_value(adj, rounded);
2611     gtk_adjustment_value_changed(adj);
2613     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2614     gtk_adjustment_set_value(adj, randomized);
2615     gtk_adjustment_value_changed(adj);
2619 void
2620 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2622     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2623     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2624     GtkWidget *l = gtk_label_new(NULL);
2625     gtk_label_set_markup(GTK_LABEL(l), title);
2626     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2627     if ( GTK_IS_TOOLBAR(tbl) ) {
2628         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2629     } else {
2630         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2631     }
2632     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2636 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2638     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2640     {
2641         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2642         ege_output_action_set_use_markup( act, TRUE );
2643         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2644         g_object_set_data( holder, "mode_action", act );
2645     }
2647     {
2648         EgeAdjustmentAction* eact = 0;
2649         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2650         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2652         /* Flatsided checkbox */
2653         {
2654             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2656             GtkTreeIter iter;
2657             gtk_list_store_append( model, &iter );
2658             gtk_list_store_set( model, &iter,
2659                                 0, _("Polygon"),
2660                                 1, _("Regular polygon (with one handle) instead of a star"),
2661                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2662                                 -1 );
2664             gtk_list_store_append( model, &iter );
2665             gtk_list_store_set( model, &iter,
2666                                 0, _("Star"),
2667                                 1, _("Star instead of a regular polygon (with one handle)"),
2668                                 2, INKSCAPE_ICON_DRAW_STAR,
2669                                 -1 );
2671             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2672             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2673             g_object_set_data( holder, "flat_action", act );
2675             ege_select_one_action_set_appearance( act, "full" );
2676             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2677             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2678             ege_select_one_action_set_icon_column( act, 2 );
2679             ege_select_one_action_set_icon_size( act, secondarySize );
2680             ege_select_one_action_set_tooltip_column( act, 1  );
2682             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2683             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2684         }
2686         /* Magnitude */
2687         {
2688         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2689         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2690         eact = create_adjustment_action( "MagnitudeAction",
2691                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2692                                          "/tools/shapes/star/magnitude", 3,
2693                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2694                                          3, 1024, 1, 5,
2695                                          labels, values, G_N_ELEMENTS(labels),
2696                                          sp_stb_magnitude_value_changed,
2697                                          1.0, 0 );
2698         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2699         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2700         }
2702         /* Spoke ratio */
2703         {
2704         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2705         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2706         eact = create_adjustment_action( "SpokeAction",
2707                                          _("Spoke ratio"), _("Spoke ratio:"),
2708                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2709                                          // Base radius is the same for the closest handle.
2710                                          _("Base radius to tip radius ratio"),
2711                                          "/tools/shapes/star/proportion", 0.5,
2712                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2713                                          0.01, 1.0, 0.01, 0.1,
2714                                          labels, values, G_N_ELEMENTS(labels),
2715                                          sp_stb_proportion_value_changed );
2716         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2717         g_object_set_data( holder, "prop_action", eact );
2718         }
2720         if ( !isFlatSided ) {
2721             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2722         } else {
2723             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2724         }
2726         /* Roundedness */
2727         {
2728         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2729         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2730         eact = create_adjustment_action( "RoundednessAction",
2731                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2732                                          "/tools/shapes/star/rounded", 0.0,
2733                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2734                                          -10.0, 10.0, 0.01, 0.1,
2735                                          labels, values, G_N_ELEMENTS(labels),
2736                                          sp_stb_rounded_value_changed );
2737         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2738         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2739         }
2741         /* Randomization */
2742         {
2743         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2744         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2745         eact = create_adjustment_action( "RandomizationAction",
2746                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2747                                          "/tools/shapes/star/randomized", 0.0,
2748                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2749                                          -10.0, 10.0, 0.001, 0.01,
2750                                          labels, values, G_N_ELEMENTS(labels),
2751                                          sp_stb_randomized_value_changed, 0.1, 3 );
2752         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2753         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2754         }
2755     }
2757     {
2758         /* Reset */
2759         {
2760             GtkAction* act = gtk_action_new( "StarResetAction",
2761                                              _("Defaults"),
2762                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2763                                              GTK_STOCK_CLEAR );
2764             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2765             gtk_action_group_add_action( mainActions, act );
2766             gtk_action_set_sensitive( act, TRUE );
2767         }
2768     }
2770     sigc::connection *connection = new sigc::connection(
2771         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2772         );
2773     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2774     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2778 //########################
2779 //##       Rect         ##
2780 //########################
2782 static void sp_rtb_sensitivize( GObject *tbl )
2784     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2785     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2786     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2788     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2789         gtk_action_set_sensitive( not_rounded, FALSE );
2790     } else {
2791         gtk_action_set_sensitive( not_rounded, TRUE );
2792     }
2796 static void
2797 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2798                           void (*setter)(SPRect *, gdouble))
2800     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2802     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2803     SPUnit const *unit = tracker->getActiveUnit();
2805     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2806         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2807         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2808     }
2810     // quit if run by the attr_changed listener
2811     if (g_object_get_data( tbl, "freeze" )) {
2812         return;
2813     }
2815     // in turn, prevent listener from responding
2816     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2818     bool modmade = false;
2819     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2820     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2821         if (SP_IS_RECT(items->data)) {
2822             if (adj->value != 0) {
2823                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2824             } else {
2825                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2826             }
2827             modmade = true;
2828         }
2829     }
2831     sp_rtb_sensitivize( tbl );
2833     if (modmade) {
2834         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2835                                    _("Change rectangle"));
2836     }
2838     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2841 static void
2842 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2844     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2847 static void
2848 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2850     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2853 static void
2854 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2856     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2859 static void
2860 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2862     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2867 static void
2868 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2870     GtkAdjustment *adj = 0;
2872     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2873     gtk_adjustment_set_value(adj, 0.0);
2874     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2875     gtk_adjustment_value_changed(adj);
2877     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2878     gtk_adjustment_set_value(adj, 0.0);
2879     gtk_adjustment_value_changed(adj);
2881     sp_rtb_sensitivize( obj );
2884 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2885                                        gchar const */*old_value*/, gchar const */*new_value*/,
2886                                        bool /*is_interactive*/, gpointer data)
2888     GObject *tbl = G_OBJECT(data);
2890     // quit if run by the _changed callbacks
2891     if (g_object_get_data( tbl, "freeze" )) {
2892         return;
2893     }
2895     // in turn, prevent callbacks from responding
2896     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2898     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2899     SPUnit const *unit = tracker->getActiveUnit();
2901     gpointer item = g_object_get_data( tbl, "item" );
2902     if (item && SP_IS_RECT(item)) {
2903         {
2904             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2905             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2906             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2907         }
2909         {
2910             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2911             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2912             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2913         }
2915         {
2916             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2917             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2918             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2919         }
2921         {
2922             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2923             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2924             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2925         }
2926     }
2928     sp_rtb_sensitivize( tbl );
2930     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2934 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2935     NULL, /* child_added */
2936     NULL, /* child_removed */
2937     rect_tb_event_attr_changed,
2938     NULL, /* content_changed */
2939     NULL  /* order_changed */
2940 };
2942 /**
2943  *  \param selection should not be NULL.
2944  */
2945 static void
2946 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2948     int n_selected = 0;
2949     Inkscape::XML::Node *repr = NULL;
2950     SPItem *item = NULL;
2952     if ( g_object_get_data( tbl, "repr" ) ) {
2953         g_object_set_data( tbl, "item", NULL );
2954     }
2955     purge_repr_listener( tbl, tbl );
2957     for (GSList const *items = selection->itemList();
2958          items != NULL;
2959          items = items->next) {
2960         if (SP_IS_RECT((SPItem *) items->data)) {
2961             n_selected++;
2962             item = (SPItem *) items->data;
2963             repr = SP_OBJECT_REPR(item);
2964         }
2965     }
2967     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2969     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2971     if (n_selected == 0) {
2972         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2974         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2975         gtk_action_set_sensitive(w, FALSE);
2976         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2977         gtk_action_set_sensitive(h, FALSE);
2979     } else if (n_selected == 1) {
2980         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2981         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2983         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2984         gtk_action_set_sensitive(w, TRUE);
2985         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2986         gtk_action_set_sensitive(h, TRUE);
2988         if (repr) {
2989             g_object_set_data( tbl, "repr", repr );
2990             g_object_set_data( tbl, "item", item );
2991             Inkscape::GC::anchor(repr);
2992             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2993             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2994         }
2995     } else {
2996         // FIXME: implement averaging of all parameters for multiple selected
2997         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2998         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2999         sp_rtb_sensitivize( tbl );
3000     }
3004 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3006     EgeAdjustmentAction* eact = 0;
3007     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3009     {
3010         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3011         ege_output_action_set_use_markup( act, TRUE );
3012         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3013         g_object_set_data( holder, "mode_action", act );
3014     }
3016     // rx/ry units menu: create
3017     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3018     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3019     // fixme: add % meaning per cent of the width/height
3020     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3021     g_object_set_data( holder, "tracker", tracker );
3023     /* W */
3024     {
3025         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3026         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3027         eact = create_adjustment_action( "RectWidthAction",
3028                                          _("Width"), _("W:"), _("Width of rectangle"),
3029                                          "/tools/shapes/rect/width", 0,
3030                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3031                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3032                                          labels, values, G_N_ELEMENTS(labels),
3033                                          sp_rtb_width_value_changed );
3034         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3035         g_object_set_data( holder, "width_action", eact );
3036         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3037         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3038     }
3040     /* H */
3041     {
3042         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3043         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3044         eact = create_adjustment_action( "RectHeightAction",
3045                                          _("Height"), _("H:"), _("Height of rectangle"),
3046                                          "/tools/shapes/rect/height", 0,
3047                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3048                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3049                                          labels, values, G_N_ELEMENTS(labels),
3050                                          sp_rtb_height_value_changed );
3051         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3052         g_object_set_data( holder, "height_action", eact );
3053         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3054         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3055     }
3057     /* rx */
3058     {
3059         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3060         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3061         eact = create_adjustment_action( "RadiusXAction",
3062                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3063                                          "/tools/shapes/rect/rx", 0,
3064                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3065                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3066                                          labels, values, G_N_ELEMENTS(labels),
3067                                          sp_rtb_rx_value_changed);
3068         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3069         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3070     }
3072     /* ry */
3073     {
3074         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3075         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3076         eact = create_adjustment_action( "RadiusYAction",
3077                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3078                                          "/tools/shapes/rect/ry", 0,
3079                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3080                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3081                                          labels, values, G_N_ELEMENTS(labels),
3082                                          sp_rtb_ry_value_changed);
3083         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3084         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3085     }
3087     // add the units menu
3088     {
3089         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3090         gtk_action_group_add_action( mainActions, act );
3091     }
3093     /* Reset */
3094     {
3095         InkAction* inky = ink_action_new( "RectResetAction",
3096                                           _("Not rounded"),
3097                                           _("Make corners sharp"),
3098                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3099                                           secondarySize );
3100         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3101         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3102         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3103         g_object_set_data( holder, "not_rounded", inky );
3104     }
3106     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3107     sp_rtb_sensitivize( holder );
3109     sigc::connection *connection = new sigc::connection(
3110         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3111         );
3112     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3113     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3116 //########################
3117 //##       3D Box       ##
3118 //########################
3120 // normalize angle so that it lies in the interval [0,360]
3121 static double box3d_normalize_angle (double a) {
3122     double angle = a + ((int) (a/360.0))*360;
3123     if (angle < 0) {
3124         angle += 360.0;
3125     }
3126     return angle;
3129 static void
3130 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3131                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3132     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3133     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3134     //       are reset).
3135     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3137     if (is_infinite) {
3138         gtk_toggle_action_set_active(tact, TRUE);
3139         gtk_action_set_sensitive(act, TRUE);
3141         double angle = persp3d_get_infinite_angle(persp, axis);
3142         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3143             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3144         }
3145     } else {
3146         gtk_toggle_action_set_active(tact, FALSE);
3147         gtk_action_set_sensitive(act, FALSE);
3148     }
3151 static void
3152 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3153     if (!persp_repr) {
3154         g_print ("No perspective given to box3d_resync_toolbar().\n");
3155         return;
3156     }
3158     GtkWidget *tbl = GTK_WIDGET(data);
3159     GtkAdjustment *adj = 0;
3160     GtkAction *act = 0;
3161     GtkToggleAction *tact = 0;
3162     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3163     if (!persp) {
3164         // Hmm, is it an error if this happens?
3165         return;
3166     }
3167     {
3168         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3169         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3170         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3172         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3173     }
3174     {
3175         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3176         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3177         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3179         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3180     }
3181     {
3182         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3183         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3184         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3186         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3187     }
3190 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3191                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3192                                                   bool /*is_interactive*/, gpointer data)
3194     GtkWidget *tbl = GTK_WIDGET(data);
3196     // quit if run by the attr_changed or selection changed listener
3197     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3198         return;
3199     }
3201     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3202     // sp_document_maybe_done() when the document is undo insensitive)
3203     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3205     // TODO: Only update the appropriate part of the toolbar
3206 //    if (!strcmp(name, "inkscape:vp_z")) {
3207         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3208 //    }
3210     Persp3D *persp = persp3d_get_from_repr(repr);
3211     persp3d_update_box_reprs(persp);
3213     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3216 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3218     NULL, /* child_added */
3219     NULL, /* child_removed */
3220     box3d_persp_tb_event_attr_changed,
3221     NULL, /* content_changed */
3222     NULL  /* order_changed */
3223 };
3225 /**
3226  *  \param selection Should not be NULL.
3227  */
3228 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3229 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3230 static void
3231 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3233     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3234     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3235     // update the perspectives with infinite VPs and leave the other ones untouched).
3237     Inkscape::XML::Node *persp_repr = NULL;
3238     purge_repr_listener(tbl, tbl);
3240     SPItem *item = selection->singleItem();
3241     if (item && SP_IS_BOX3D(item)) {
3242         // FIXME: Also deal with multiple selected boxes
3243         SPBox3D *box = SP_BOX3D(item);
3244         Persp3D *persp = box3d_get_perspective(box);
3245         persp_repr = SP_OBJECT_REPR(persp);
3246         if (persp_repr) {
3247             g_object_set_data(tbl, "repr", persp_repr);
3248             Inkscape::GC::anchor(persp_repr);
3249             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3250             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3251         }
3253         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3254         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3255         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3257         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3258         box3d_resync_toolbar(persp_repr, tbl);
3259         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3260     }
3263 static void
3264 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3266     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3267     SPDocument *document = sp_desktop_document(desktop);
3269     // quit if run by the attr_changed or selection changed listener
3270     if (g_object_get_data( dataKludge, "freeze" )) {
3271         return;
3272     }
3274     // in turn, prevent listener from responding
3275     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3277     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3278     if (sel_persps.empty()) {
3279         // this can happen when the document is created; we silently ignore it
3280         return;
3281     }
3282     Persp3D *persp = sel_persps.front();
3284     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3285     SP_OBJECT(persp)->updateRepr();
3287     // TODO: use the correct axis here, too
3288     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3290     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3294 static void
3295 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3297     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3300 static void
3301 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3303     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3306 static void
3307 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3309     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3313 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3315     // TODO: Take all selected perspectives into account
3316     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3317     if (sel_persps.empty()) {
3318         // this can happen when the document is created; we silently ignore it
3319         return;
3320     }
3321     Persp3D *persp = sel_persps.front();
3323     bool set_infinite = gtk_toggle_action_get_active(act);
3324     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3327 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3329     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3332 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3334     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3337 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3339     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3342 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3344     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3345     EgeAdjustmentAction* eact = 0;
3346     SPDocument *document = sp_desktop_document (desktop);
3347     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3349     EgeAdjustmentAction* box3d_angle_x = 0;
3350     EgeAdjustmentAction* box3d_angle_y = 0;
3351     EgeAdjustmentAction* box3d_angle_z = 0;
3353     /* Angle X */
3354     {
3355         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3356         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3357         eact = create_adjustment_action( "3DBoxAngleXAction",
3358                                          _("Angle in X direction"), _("Angle X:"),
3359                                          // Translators: PL is short for 'perspective line'
3360                                          _("Angle of PLs in X direction"),
3361                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3362                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3363                                          -360.0, 360.0, 1.0, 10.0,
3364                                          labels, values, G_N_ELEMENTS(labels),
3365                                          box3d_angle_x_value_changed );
3366         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3367         g_object_set_data( holder, "box3d_angle_x_action", eact );
3368         box3d_angle_x = eact;
3369     }
3371     if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3372         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3373     } else {
3374         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3375     }
3378     /* VP X state */
3379     {
3380         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3381                                                       // Translators: VP is short for 'vanishing point'
3382                                                       _("State of VP in X direction"),
3383                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3384                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3385                                                       Inkscape::ICON_SIZE_DECORATION );
3386         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3387         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3388         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3389         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3390         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3391     }
3393     /* Angle Y */
3394     {
3395         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3396         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3397         eact = create_adjustment_action( "3DBoxAngleYAction",
3398                                          _("Angle in Y direction"), _("Angle Y:"),
3399                                          // Translators: PL is short for 'perspective line'
3400                                          _("Angle of PLs in Y direction"),
3401                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3402                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3403                                          -360.0, 360.0, 1.0, 10.0,
3404                                          labels, values, G_N_ELEMENTS(labels),
3405                                          box3d_angle_y_value_changed );
3406         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3407         g_object_set_data( holder, "box3d_angle_y_action", eact );
3408         box3d_angle_y = eact;
3409     }
3411     if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3412         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3413     } else {
3414         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3415     }
3417     /* VP Y state */
3418     {
3419         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3420                                                       // Translators: VP is short for 'vanishing point'
3421                                                       _("State of VP in Y direction"),
3422                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3423                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3424                                                       Inkscape::ICON_SIZE_DECORATION );
3425         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3426         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3427         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3428         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3429         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3430     }
3432     /* Angle Z */
3433     {
3434         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3435         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3436         eact = create_adjustment_action( "3DBoxAngleZAction",
3437                                          _("Angle in Z direction"), _("Angle Z:"),
3438                                          // Translators: PL is short for 'perspective line'
3439                                          _("Angle of PLs in Z direction"),
3440                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3441                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3442                                          -360.0, 360.0, 1.0, 10.0,
3443                                          labels, values, G_N_ELEMENTS(labels),
3444                                          box3d_angle_z_value_changed );
3445         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3446         g_object_set_data( holder, "box3d_angle_z_action", eact );
3447         box3d_angle_z = eact;
3448     }
3450     if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3451         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3452     } else {
3453         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3454     }
3456     /* VP Z state */
3457     {
3458         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3459                                                       // Translators: VP is short for 'vanishing point'
3460                                                       _("State of VP in Z direction"),
3461                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3462                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3463                                                       Inkscape::ICON_SIZE_DECORATION );
3464         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3465         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3466         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3467         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3468         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3469     }
3471     sigc::connection *connection = new sigc::connection(
3472         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3473        );
3474     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3475     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3478 //########################
3479 //##       Spiral       ##
3480 //########################
3482 static void
3483 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3485     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3487     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3488         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3489         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3490     }
3492     // quit if run by the attr_changed listener
3493     if (g_object_get_data( tbl, "freeze" )) {
3494         return;
3495     }
3497     // in turn, prevent listener from responding
3498     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3500     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3502     bool modmade = false;
3503     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3504          items != NULL;
3505          items = items->next)
3506     {
3507         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3508             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3509             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3510             SP_OBJECT((SPItem *) items->data)->updateRepr();
3511             modmade = true;
3512         }
3513     }
3515     g_free(namespaced_name);
3517     if (modmade) {
3518         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3519                                    _("Change spiral"));
3520     }
3522     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3525 static void
3526 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3528     sp_spl_tb_value_changed(adj, tbl, "revolution");
3531 static void
3532 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3534     sp_spl_tb_value_changed(adj, tbl, "expansion");
3537 static void
3538 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3540     sp_spl_tb_value_changed(adj, tbl, "t0");
3543 static void
3544 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3546     GtkWidget *tbl = GTK_WIDGET(obj);
3548     GtkAdjustment *adj;
3550     // fixme: make settable
3551     gdouble rev = 5;
3552     gdouble exp = 1.0;
3553     gdouble t0 = 0.0;
3555     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3556     gtk_adjustment_set_value(adj, rev);
3557     gtk_adjustment_value_changed(adj);
3559     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3560     gtk_adjustment_set_value(adj, exp);
3561     gtk_adjustment_value_changed(adj);
3563     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3564     gtk_adjustment_set_value(adj, t0);
3565     gtk_adjustment_value_changed(adj);
3567     spinbutton_defocus(GTK_OBJECT(tbl));
3571 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3572                                          gchar const */*old_value*/, gchar const */*new_value*/,
3573                                          bool /*is_interactive*/, gpointer data)
3575     GtkWidget *tbl = GTK_WIDGET(data);
3577     // quit if run by the _changed callbacks
3578     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3579         return;
3580     }
3582     // in turn, prevent callbacks from responding
3583     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3585     GtkAdjustment *adj;
3586     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3587     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3589     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3590     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3592     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3593     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3595     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3599 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3600     NULL, /* child_added */
3601     NULL, /* child_removed */
3602     spiral_tb_event_attr_changed,
3603     NULL, /* content_changed */
3604     NULL  /* order_changed */
3605 };
3607 static void
3608 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3610     int n_selected = 0;
3611     Inkscape::XML::Node *repr = NULL;
3613     purge_repr_listener( tbl, tbl );
3615     for (GSList const *items = selection->itemList();
3616          items != NULL;
3617          items = items->next)
3618     {
3619         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3620             n_selected++;
3621             repr = SP_OBJECT_REPR((SPItem *) items->data);
3622         }
3623     }
3625     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3627     if (n_selected == 0) {
3628         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3629     } else if (n_selected == 1) {
3630         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3632         if (repr) {
3633             g_object_set_data( tbl, "repr", repr );
3634             Inkscape::GC::anchor(repr);
3635             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3636             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3637         }
3638     } else {
3639         // FIXME: implement averaging of all parameters for multiple selected
3640         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3641         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3642     }
3646 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3648     EgeAdjustmentAction* eact = 0;
3649     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3651     {
3652         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3653         ege_output_action_set_use_markup( act, TRUE );
3654         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3655         g_object_set_data( holder, "mode_action", act );
3656     }
3658     /* Revolution */
3659     {
3660         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3661         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3662         eact = create_adjustment_action( "SpiralRevolutionAction",
3663                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3664                                          "/tools/shapes/spiral/revolution", 3.0,
3665                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3666                                          0.01, 1024.0, 0.1, 1.0,
3667                                          labels, values, G_N_ELEMENTS(labels),
3668                                          sp_spl_tb_revolution_value_changed, 1, 2);
3669         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3670     }
3672     /* Expansion */
3673     {
3674         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3675         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3676         eact = create_adjustment_action( "SpiralExpansionAction",
3677                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3678                                          "/tools/shapes/spiral/expansion", 1.0,
3679                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3680                                          0.0, 1000.0, 0.01, 1.0,
3681                                          labels, values, G_N_ELEMENTS(labels),
3682                                          sp_spl_tb_expansion_value_changed);
3683         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3684     }
3686     /* T0 */
3687     {
3688         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3689         gdouble values[] = {0, 0.5, 0.9};
3690         eact = create_adjustment_action( "SpiralT0Action",
3691                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3692                                          "/tools/shapes/spiral/t0", 0.0,
3693                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3694                                          0.0, 0.999, 0.01, 1.0,
3695                                          labels, values, G_N_ELEMENTS(labels),
3696                                          sp_spl_tb_t0_value_changed);
3697         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3698     }
3700     /* Reset */
3701     {
3702         InkAction* inky = ink_action_new( "SpiralResetAction",
3703                                           _("Defaults"),
3704                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3705                                           GTK_STOCK_CLEAR,
3706                                           secondarySize );
3707         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3708         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3709     }
3712     sigc::connection *connection = new sigc::connection(
3713         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3714         );
3715     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3716     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3719 //########################
3720 //##     Pen/Pencil     ##
3721 //########################
3723 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3724 static Glib::ustring const
3725 freehand_tool_name(GObject *dataKludge)
3727     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3728     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3729              ? "/tools/freehand/pen"
3730              : "/tools/freehand/pencil" );
3733 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3735     gint mode = ege_select_one_action_get_active(act);
3737     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3738     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3740     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3742     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3743     // preparatory work here
3744     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3745         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3746         sp_pen_context_set_polyline_mode(pc);
3747     }
3750 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3752     /* Freehand mode toggle buttons */
3753     {
3754         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3755         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3756         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3758         {
3759             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3761             GtkTreeIter iter;
3762             gtk_list_store_append( model, &iter );
3763             gtk_list_store_set( model, &iter,
3764                                 0, _("Bezier"),
3765                                 1, _("Create regular Bezier path"),
3766                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3767                                 -1 );
3769             gtk_list_store_append( model, &iter );
3770             gtk_list_store_set( model, &iter,
3771                                 0, _("Spiro"),
3772                                 1, _("Create Spiro path"),
3773                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3774                                 -1 );
3776             if (!tool_is_pencil) {
3777                 gtk_list_store_append( model, &iter );
3778                 gtk_list_store_set( model, &iter,
3779                                     0, _("Zigzag"),
3780                                     1, _("Create a sequence of straight line segments"),
3781                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3782                                     -1 );
3784                 gtk_list_store_append( model, &iter );
3785                 gtk_list_store_set( model, &iter,
3786                                     0, _("Paraxial"),
3787                                     1, _("Create a sequence of paraxial line segments"),
3788                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3789                                     -1 );
3790             }
3792             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3793                                                                 "FreehandModeActionPencil" :
3794                                                                 "FreehandModeActionPen",
3795                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3796             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3798             ege_select_one_action_set_appearance( act, "full" );
3799             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3800             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3801             ege_select_one_action_set_icon_column( act, 2 );
3802             ege_select_one_action_set_icon_size( act, secondarySize );
3803             ege_select_one_action_set_tooltip_column( act, 1  );
3805             ege_select_one_action_set_active( act, freehandMode);
3806             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3807         }
3808     }
3811 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3812     gint shape = ege_select_one_action_get_active( act );
3813     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3814     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3817 /**
3818  * \brief Generate the list of freehand advanced shape option entries.
3819  */
3820 GList * freehand_shape_dropdown_items_list() {
3821     GList *glist = NULL;
3823     glist = g_list_append (glist, _("None"));
3824     glist = g_list_append (glist, _("Triangle in"));
3825     glist = g_list_append (glist, _("Triangle out"));
3826     glist = g_list_append (glist, _("Ellipse"));
3827     glist = g_list_append (glist, _("From clipboard"));
3829     return glist;
3832 static void
3833 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3834     /*advanced shape options */
3835     {
3836         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3837         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3839         GList* items = 0;
3840         gint count = 0;
3841         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3842         {
3843             GtkTreeIter iter;
3844             gtk_list_store_append( model, &iter );
3845             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3846             count++;
3847         }
3848         g_list_free( items );
3849         items = 0;
3850         EgeSelectOneAction* act1 = ege_select_one_action_new(
3851             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3852             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3853         g_object_set( act1, "short_label", _("Shape:"), NULL );
3854         ege_select_one_action_set_appearance( act1, "compact" );
3855         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3856         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3857         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3858         g_object_set_data( holder, "shape_action", act1 );
3859     }
3862 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3864     sp_add_freehand_mode_toggle(mainActions, holder, false);
3865     freehand_add_advanced_shape_options(mainActions, holder, false);
3869 static void
3870 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3872     GtkWidget *tbl = GTK_WIDGET(obj);
3874     GtkAdjustment *adj;
3876     // fixme: make settable
3877     gdouble tolerance = 4;
3879     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3880     gtk_adjustment_set_value(adj, tolerance);
3881     gtk_adjustment_value_changed(adj);
3883     spinbutton_defocus(GTK_OBJECT(tbl));
3886 static void
3887 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3889     // quit if run by the attr_changed listener
3890     if (g_object_get_data( tbl, "freeze" )) {
3891         return;
3892     }
3893     // in turn, prevent listener from responding
3894     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3895     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3896     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3897     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3900 /*
3901 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3902 public:
3903     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3904     {
3905         g_object_set_data(_obj, "prefobserver", this);
3906     }
3907     virtual ~PencilToleranceObserver() {
3908         if (g_object_get_data(_obj, "prefobserver") == this) {
3909             g_object_set_data(_obj, "prefobserver", NULL);
3910         }
3911     }
3912     virtual void notify(Inkscape::Preferences::Entry const &val) {
3913         GObject* tbl = _obj;
3914         if (g_object_get_data( tbl, "freeze" )) {
3915             return;
3916         }
3917         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3919         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3921         double v = val.getDouble(adj->value);
3922         gtk_adjustment_set_value(adj, v);
3923         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3924     }
3925 private:
3926     GObject *_obj;
3927 };
3928 */
3930 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3932     sp_add_freehand_mode_toggle(mainActions, holder, true);
3934     EgeAdjustmentAction* eact = 0;
3936     /* Tolerance */
3937     {
3938         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3939         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3940         eact = create_adjustment_action( "PencilToleranceAction",
3941                                          _("Smoothing:"), _("Smoothing: "),
3942                  _("How much smoothing (simplifying) is applied to the line"),
3943                                          "/tools/freehand/pencil/tolerance",
3944                                          3.0,
3945                                          GTK_WIDGET(desktop->canvas), NULL,
3946                                          holder, TRUE, "altx-pencil",
3947                                          1, 100.0, 0.5, 1.0,
3948                                          labels, values, G_N_ELEMENTS(labels),
3949                                          sp_pencil_tb_tolerance_value_changed,
3950                                          1, 2);
3951         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3952         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3953     }
3955     /* advanced shape options */
3956     freehand_add_advanced_shape_options(mainActions, holder, true);
3958     /* Reset */
3959     {
3960         InkAction* inky = ink_action_new( "PencilResetAction",
3961                                           _("Defaults"),
3962                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3963                                           GTK_STOCK_CLEAR,
3964                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3965         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3966         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3967     }
3969     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3974 //########################
3975 //##       Tweak        ##
3976 //########################
3978 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3980     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3981     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
3984 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3986     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3987     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
3990 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3992     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3993     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
3996 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3998     int mode = ege_select_one_action_get_active( act );
3999     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4000     prefs->setInt("/tools/tweak/mode", mode);
4002     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4003     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4004     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4005     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4006     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4007     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4008     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4009         if (doh) gtk_action_set_sensitive (doh, TRUE);
4010         if (dos) gtk_action_set_sensitive (dos, TRUE);
4011         if (dol) gtk_action_set_sensitive (dol, TRUE);
4012         if (doo) gtk_action_set_sensitive (doo, TRUE);
4013         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4014         if (fid) gtk_action_set_sensitive (fid, FALSE);
4015     } else {
4016         if (doh) gtk_action_set_sensitive (doh, FALSE);
4017         if (dos) gtk_action_set_sensitive (dos, FALSE);
4018         if (dol) gtk_action_set_sensitive (dol, FALSE);
4019         if (doo) gtk_action_set_sensitive (doo, FALSE);
4020         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4021         if (fid) gtk_action_set_sensitive (fid, TRUE);
4022     }
4025 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4027     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4028     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4031 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4032     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4033     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4035 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4036     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4037     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4039 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4040     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4041     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4043 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4044     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4045     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4048 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4050     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4051     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4053     {
4054         /* Width */
4055         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4056         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4057         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4058                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4059                                                               "/tools/tweak/width", 15,
4060                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4061                                                               1, 100, 1.0, 10.0,
4062                                                               labels, values, G_N_ELEMENTS(labels),
4063                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4064         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4065         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4066         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4067     }
4070     {
4071         /* Force */
4072         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4073         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4074         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4075                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4076                                                               "/tools/tweak/force", 20,
4077                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4078                                                               1, 100, 1.0, 10.0,
4079                                                               labels, values, G_N_ELEMENTS(labels),
4080                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4081         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4082         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4083         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4084     }
4086     /* Mode */
4087     {
4088         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4090         GtkTreeIter iter;
4091         gtk_list_store_append( model, &iter );
4092         gtk_list_store_set( model, &iter,
4093                             0, _("Move mode"),
4094                             1, _("Move objects in any direction"),
4095                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4096                             -1 );
4098         gtk_list_store_append( model, &iter );
4099         gtk_list_store_set( model, &iter,
4100                             0, _("Move in/out mode"),
4101                             1, _("Move objects towards cursor; with Shift from cursor"),
4102                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4103                             -1 );
4105         gtk_list_store_append( model, &iter );
4106         gtk_list_store_set( model, &iter,
4107                             0, _("Move jitter mode"),
4108                             1, _("Move objects in random directions"),
4109                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4110                             -1 );
4112         gtk_list_store_append( model, &iter );
4113         gtk_list_store_set( model, &iter,
4114                             0, _("Scale mode"),
4115                             1, _("Shrink objects, with Shift enlarge"),
4116                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4117                             -1 );
4119         gtk_list_store_append( model, &iter );
4120         gtk_list_store_set( model, &iter,
4121                             0, _("Rotate mode"),
4122                             1, _("Rotate objects, with Shift counterclockwise"),
4123                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4124                             -1 );
4126         gtk_list_store_append( model, &iter );
4127         gtk_list_store_set( model, &iter,
4128                             0, _("Duplicate/delete mode"),
4129                             1, _("Duplicate objects, with Shift delete"),
4130                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4131                             -1 );
4133         gtk_list_store_append( model, &iter );
4134         gtk_list_store_set( model, &iter,
4135                             0, _("Push mode"),
4136                             1, _("Push parts of paths in any direction"),
4137                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4138                             -1 );
4140         gtk_list_store_append( model, &iter );
4141         gtk_list_store_set( model, &iter,
4142                             0, _("Shrink/grow mode"),
4143                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4144                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4145                             -1 );
4147         gtk_list_store_append( model, &iter );
4148         gtk_list_store_set( model, &iter,
4149                             0, _("Attract/repel mode"),
4150                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4151                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4152                             -1 );
4154         gtk_list_store_append( model, &iter );
4155         gtk_list_store_set( model, &iter,
4156                             0, _("Roughen mode"),
4157                             1, _("Roughen parts of paths"),
4158                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4159                             -1 );
4161         gtk_list_store_append( model, &iter );
4162         gtk_list_store_set( model, &iter,
4163                             0, _("Color paint mode"),
4164                             1, _("Paint the tool's color upon selected objects"),
4165                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4166                             -1 );
4168         gtk_list_store_append( model, &iter );
4169         gtk_list_store_set( model, &iter,
4170                             0, _("Color jitter mode"),
4171                             1, _("Jitter the colors of selected objects"),
4172                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4173                             -1 );
4175         gtk_list_store_append( model, &iter );
4176         gtk_list_store_set( model, &iter,
4177                             0, _("Blur mode"),
4178                             1, _("Blur selected objects more; with Shift, blur less"),
4179                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4180                             -1 );
4183         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4184         g_object_set( act, "short_label", _("Mode:"), NULL );
4185         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4186         g_object_set_data( holder, "mode_action", act );
4188         ege_select_one_action_set_appearance( act, "full" );
4189         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4190         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4191         ege_select_one_action_set_icon_column( act, 2 );
4192         ege_select_one_action_set_icon_size( act, secondarySize );
4193         ege_select_one_action_set_tooltip_column( act, 1  );
4195         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4196         ege_select_one_action_set_active( act, mode );
4197         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4199         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4200     }
4202     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4204     {
4205         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4206         ege_output_action_set_use_markup( act, TRUE );
4207         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4208         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4209             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4210         g_object_set_data( holder, "tweak_channels_label", act);
4211     }
4213     {
4214         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4215                                                       _("Hue"),
4216                                                       _("In color mode, act on objects' hue"),
4217                                                       NULL,
4218                                                       Inkscape::ICON_SIZE_DECORATION );
4219         //TRANSLATORS:  "H" here stands for hue
4220         g_object_set( act, "short_label", _("H"), NULL );
4221         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4222         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4223         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4224         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4225             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4226         g_object_set_data( holder, "tweak_doh", act);
4227     }
4228     {
4229         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4230                                                       _("Saturation"),
4231                                                       _("In color mode, act on objects' saturation"),
4232                                                       NULL,
4233                                                       Inkscape::ICON_SIZE_DECORATION );
4234         //TRANSLATORS: "S" here stands for Saturation
4235         g_object_set( act, "short_label", _("S"), NULL );
4236         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4237         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4238         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4239         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4240             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4241         g_object_set_data( holder, "tweak_dos", act );
4242     }
4243     {
4244         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4245                                                       _("Lightness"),
4246                                                       _("In color mode, act on objects' lightness"),
4247                                                       NULL,
4248                                                       Inkscape::ICON_SIZE_DECORATION );
4249         //TRANSLATORS: "L" here stands for Lightness
4250         g_object_set( act, "short_label", _("L"), NULL );
4251         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4252         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4253         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4254         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4255             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4256         g_object_set_data( holder, "tweak_dol", act );
4257     }
4258     {
4259         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4260                                                       _("Opacity"),
4261                                                       _("In color mode, act on objects' opacity"),
4262                                                       NULL,
4263                                                       Inkscape::ICON_SIZE_DECORATION );
4264         //TRANSLATORS: "O" here stands for Opacity
4265         g_object_set( act, "short_label", _("O"), NULL );
4266         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4267         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4268         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4269         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4270             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4271         g_object_set_data( holder, "tweak_doo", act );
4272     }
4274     {   /* Fidelity */
4275         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4276         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4277         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4278                                                               _("Fidelity"), _("Fidelity:"),
4279                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4280                                                               "/tools/tweak/fidelity", 50,
4281                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4282                                                               1, 100, 1.0, 10.0,
4283                                                               labels, values, G_N_ELEMENTS(labels),
4284                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4285         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4286         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4287         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4288             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4289         g_object_set_data( holder, "tweak_fidelity", eact );
4290     }
4293     /* Use Pressure button */
4294     {
4295         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4296                                                       _("Pressure"),
4297                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4298                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4299                                                       Inkscape::ICON_SIZE_DECORATION );
4300         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4302         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4303     }
4308 //########################
4309 //##       Spray        ##
4310 //########################
4312 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4314     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4315     prefs->setDouble( "/tools/spray/width", adj->value );
4318 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4320     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4321     prefs->setDouble( "/tools/spray/mean", adj->value );
4324 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4326     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4327     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4330 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4332     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4333     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4336 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4338     int mode = ege_select_one_action_get_active( act );
4339     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4340     prefs->setInt("/tools/spray/mode", mode);
4343 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4345     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4346     prefs->setDouble( "/tools/spray/population", adj->value );
4349 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4351     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4352     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4355 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4357     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4358     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4362 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4364     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4365     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4367     {
4368         /* Width */
4369         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4370         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4371         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4372                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4373                                                               "/tools/spray/width", 15,
4374                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4375                                                               1, 100, 1.0, 10.0,
4376                                                               labels, values, G_N_ELEMENTS(labels),
4377                                                               sp_spray_width_value_changed,  1, 0 );
4378         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4379         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4380         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4381     }
4383     {
4384         /* Mean */
4385         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4386         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4387         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4388                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4389                                                               "/tools/spray/mean", 0,
4390                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4391                                                               0, 100, 1.0, 10.0,
4392                                                               labels, values, G_N_ELEMENTS(labels),
4393                                                               sp_spray_mean_value_changed,  1, 0 );
4394         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4395         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4396         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4397     }
4399     {
4400         /* Standard_deviation */
4401         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4402         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4403         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4404                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4405                                                               "/tools/spray/standard_deviation", 70,
4406                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4407                                                               1, 100, 1.0, 10.0,
4408                                                               labels, values, G_N_ELEMENTS(labels),
4409                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4410         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4411         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4412         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4413     }
4415     /* Mode */
4416     {
4417         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4419         GtkTreeIter iter;
4420         gtk_list_store_append( model, &iter );
4421         gtk_list_store_set( model, &iter,
4422                             0, _("Spray with copies"),
4423                             1, _("Spray copies of the initial selection"),
4424                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4425                             -1 );
4427         gtk_list_store_append( model, &iter );
4428         gtk_list_store_set( model, &iter,
4429                             0, _("Spray with clones"),
4430                             1, _("Spray clones of the initial selection"),
4431                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4432                             -1 );
4434         gtk_list_store_append( model, &iter );
4435         gtk_list_store_set( model, &iter,
4436                             0, _("Spray single path"),
4437                             1, _("Spray objects in a single path"),
4438                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4439                             -1 );
4441         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4442         g_object_set( act, "short_label", _("Mode:"), NULL );
4443         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4444         g_object_set_data( holder, "mode_action", act );
4446         ege_select_one_action_set_appearance( act, "full" );
4447         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4448         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4449         ege_select_one_action_set_icon_column( act, 2 );
4450         ege_select_one_action_set_icon_size( act, secondarySize );
4451         ege_select_one_action_set_tooltip_column( act, 1  );
4453         gint mode = prefs->getInt("/tools/spray/mode", 1);
4454         ege_select_one_action_set_active( act, mode );
4455         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4457         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4458     }
4460     {   /* Population */
4461         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4462         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4463         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4464                                                               _("Amount"), _("Amount:"),
4465                                                               _("Adjusts the number of items sprayed per clic."),
4466                                                               "/tools/spray/population", 70,
4467                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4468                                                               1, 100, 1.0, 10.0,
4469                                                               labels, values, G_N_ELEMENTS(labels),
4470                                                               sp_spray_population_value_changed,  1, 0 );
4471         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4472         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4473         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4474         g_object_set_data( holder, "spray_population", eact );
4475     }
4477     /* Use Pressure button */
4478     {
4479         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4480                                                       _("Pressure"),
4481                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4482                                                       "use_pressure",
4483                                                       Inkscape::ICON_SIZE_DECORATION );
4484         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4485         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4486         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4487     }
4489     {   /* Rotation */
4490         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4491         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4492         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4493                                                               _("Rotation"), _("Rotation:"),
4494                                                               // xgettext:no-c-format
4495                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4496                                                               "/tools/spray/rotation_variation", 0,
4497                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4498                                                               0, 100, 1.0, 10.0,
4499                                                               labels, values, G_N_ELEMENTS(labels),
4500                                                               sp_spray_rotation_value_changed,  1, 0 );
4501         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4502         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4503         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4504         g_object_set_data( holder, "spray_rotation", eact );
4505     }
4507     {   /* Scale */
4508         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4509         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4510         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4511                                                               _("Scale"), _("Scale:"),
4512                                                               // xgettext:no-c-format
4513                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4514                                                               "/tools/spray/scale_variation", 0,
4515                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4516                                                               0, 100, 1.0, 10.0,
4517                                                               labels, values, G_N_ELEMENTS(labels),
4518                                                               sp_spray_scale_value_changed,  1, 0 );
4519         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4520         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4521         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4522         g_object_set_data( holder, "spray_scale", eact );
4523     }
4530 //########################
4531 //##     Calligraphy    ##
4532 //########################
4533 static void update_presets_list (GObject *tbl)
4535     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4536     if (g_object_get_data(tbl, "presets_blocked"))
4537         return;
4539     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4540     if (!sel) {
4541         // WTF!? This will cause a segfault if ever reached
4542         //ege_select_one_action_set_active(sel, 0);
4543         return;
4544     }
4546     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4548     int ege_index = 1;
4549     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4550         bool match = true;
4552         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4553         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4554             Glib::ustring entry_name = j->getEntryName();
4555             if (entry_name == "id" || entry_name == "name") continue;
4557             void *widget = g_object_get_data(tbl, entry_name.data());
4558             if (widget) {
4559                 if (GTK_IS_ADJUSTMENT(widget)) {
4560                     double v = j->getDouble();
4561                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4562                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4563                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4564                         match = false;
4565                         break;
4566                     }
4567                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4568                     bool v = j->getBool();
4569                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4570                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4571                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4572                         match = false;
4573                         break;
4574                     }
4575                 }
4576             }
4577         }
4579         if (match) {
4580             // newly added item is at the same index as the
4581             // save command, so we need to change twice for it to take effect
4582             ege_select_one_action_set_active(sel, 0);
4583             ege_select_one_action_set_active(sel, ege_index); // one-based index
4584             return;
4585         }
4586     }
4588     // no match found
4589     ege_select_one_action_set_active(sel, 0);
4592 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4594     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4595     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4596     update_presets_list(tbl);
4599 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4601     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4602     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4603     update_presets_list(tbl);
4606 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4608     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4609     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4610     update_presets_list(tbl);
4613 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4615     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4616     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4617     update_presets_list(tbl);
4620 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4622     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4623     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4624     update_presets_list(tbl);
4627 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4629     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4630     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4631     update_presets_list(tbl);
4634 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4636     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4637     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4638     update_presets_list(tbl);
4641 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4643     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4644     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4645     update_presets_list(tbl);
4648 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4650     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4651     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4652     update_presets_list(tbl);
4655 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4657     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4658     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4659     update_presets_list(tbl);
4662 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4664     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4665     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4666     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4667     update_presets_list(tbl);
4668     if (calligraphy_angle )
4669         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4673 static gchar const *const widget_names[] = {
4674     "width",
4675     "mass",
4676     "wiggle",
4677     "angle",
4678     "thinning",
4679     "tremor",
4680     "flatness",
4681     "cap_rounding",
4682     "usepressure",
4683     "tracebackground",
4684     "usetilt"
4685 };
4688 static void sp_dcc_build_presets_list(GObject *tbl)
4690     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4692     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4693     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4694     gtk_list_store_clear (model);
4696     {
4697         GtkTreeIter iter;
4698         gtk_list_store_append( model, &iter );
4699         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4700     }
4702     // iterate over all presets to populate the list
4703     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4704     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4705     int ii=1;
4707     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4708         GtkTreeIter iter;
4709         Glib::ustring preset_name = prefs->getString(*i + "/name");
4710         gtk_list_store_append( model, &iter );
4711         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4712     }
4714     {
4715         GtkTreeIter iter;
4716         gtk_list_store_append( model, &iter );
4717         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4718         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4719     }
4721     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4723     update_presets_list (tbl);
4726 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4728     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4729     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4730     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4731     if (! desktop) return;
4733     if (g_object_get_data(tbl, "presets_blocked"))
4734         return;
4736     CalligraphicProfileRename::show(desktop);
4737     if ( !CalligraphicProfileRename::applied()) {
4738         // dialog cancelled
4739         update_presets_list (tbl);
4740         return;
4741     }
4742     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4744     if (profile_name.empty()) {
4745         // empty name entered
4746         update_presets_list (tbl);
4747         return;
4748     }
4750     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4752     // If there's a preset with the given name, find it and set save_path appropriately
4753     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4754     int total_presets = presets.size();
4755     int new_index = -1;
4756     Glib::ustring save_path; // profile pref path without a trailing slash
4758     int temp_index = 0;
4759     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4760         Glib::ustring name = prefs->getString(*i + "/name");
4761         if (!name.empty() && profile_name == name) {
4762             new_index = temp_index;
4763             save_path = *i;
4764             break;
4765         }
4766     }
4768     if (new_index == -1) {
4769         // no preset with this name, create
4770         new_index = total_presets + 1;
4771         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4772         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4773         g_free(profile_id);
4774     }
4776     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4777         gchar const *const widget_name = widget_names[i];
4778         void *widget = g_object_get_data(tbl, widget_name);
4779         if (widget) {
4780             if (GTK_IS_ADJUSTMENT(widget)) {
4781                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4782                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4783                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4784             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4785                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4786                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4787                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4788             } else {
4789                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4790             }
4791         } else {
4792             g_warning("Bad key when writing preset: %s\n", widget_name);
4793         }
4794     }
4795     prefs->setString(save_path + "/name", profile_name);
4797     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4798     sp_dcc_build_presets_list (tbl);
4802 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4804     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4806     gint preset_index = ege_select_one_action_get_active( act );
4807     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4808     // even when the preset is not changed. It would be good to replace it with something more
4809     // modern. Index 0 means "No preset", so we don't do anything.
4810     if (preset_index == 0) return;
4812     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4814     if (preset_index == save_presets_index) {
4815         // this is the Save command
4816         sp_dcc_save_profile(NULL, tbl);
4817         return;
4818     }
4820     if (g_object_get_data(tbl, "presets_blocked"))
4821         return;
4823     // preset_index is one-based so we subtract 1
4824     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4825     Glib::ustring preset_path = presets.at(preset_index - 1);
4827     if (!preset_path.empty()) {
4828         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
4830         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4832         // Shouldn't this be std::map?
4833         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4834             Glib::ustring entry_name = i->getEntryName();
4835             if (entry_name == "id" || entry_name == "name") continue;
4836             void *widget = g_object_get_data(tbl, entry_name.data());
4837             if (widget) {
4838                 if (GTK_IS_ADJUSTMENT(widget)) {
4839                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4840                     gtk_adjustment_set_value(adj, i->getDouble());
4841                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4842                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4843                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4844                     gtk_toggle_action_set_active(toggle, i->getBool());
4845                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4846                 } else {
4847                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4848                 }
4849             } else {
4850                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4851             }
4852         }
4853         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4854     }
4858 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4860     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4861     {
4862         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4864         EgeAdjustmentAction* calligraphy_angle = 0;
4866         {
4867         /* Width */
4868         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4869         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4870         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4871                                                               _("Pen Width"), _("Width:"),
4872                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4873                                                               "/tools/calligraphic/width", 15,
4874                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4875                                                               1, 100, 1.0, 10.0,
4876                                                               labels, values, G_N_ELEMENTS(labels),
4877                                                               sp_ddc_width_value_changed,  1, 0 );
4878         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4879         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4880         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4881         }
4883         {
4884         /* Thinning */
4885             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4886             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4887         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4888                                                               _("Stroke Thinning"), _("Thinning:"),
4889                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4890                                                               "/tools/calligraphic/thinning", 10,
4891                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4892                                                               -100, 100, 1, 10.0,
4893                                                               labels, values, G_N_ELEMENTS(labels),
4894                                                               sp_ddc_velthin_value_changed, 1, 0);
4895         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4896         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4897         }
4899         {
4900         /* Angle */
4901         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4902         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4903         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4904                                                               _("Pen Angle"), _("Angle:"),
4905                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4906                                                               "/tools/calligraphic/angle", 30,
4907                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4908                                                               -90.0, 90.0, 1.0, 10.0,
4909                                                               labels, values, G_N_ELEMENTS(labels),
4910                                                               sp_ddc_angle_value_changed, 1, 0 );
4911         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4912         g_object_set_data( holder, "angle_action", eact );
4913         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4914         calligraphy_angle = eact;
4915         }
4917         {
4918         /* Fixation */
4919             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4920         gdouble values[] = {0, 20, 40, 60, 90, 100};
4921         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4922                                                               _("Fixation"), _("Fixation:"),
4923                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4924                                                               "/tools/calligraphic/flatness", 90,
4925                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4926                                                               0.0, 100, 1.0, 10.0,
4927                                                               labels, values, G_N_ELEMENTS(labels),
4928                                                               sp_ddc_flatness_value_changed, 1, 0);
4929         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4930         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4931         }
4933         {
4934         /* Cap Rounding */
4935             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4936         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4937         // TRANSLATORS: "cap" means "end" (both start and finish) here
4938         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4939                                                               _("Cap rounding"), _("Caps:"),
4940                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4941                                                               "/tools/calligraphic/cap_rounding", 0.0,
4942                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4943                                                               0.0, 5.0, 0.01, 0.1,
4944                                                               labels, values, G_N_ELEMENTS(labels),
4945                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4946         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4947         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4948         }
4950         {
4951         /* Tremor */
4952             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4953         gdouble values[] = {0, 10, 20, 40, 60, 100};
4954         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4955                                                               _("Stroke Tremor"), _("Tremor:"),
4956                                                               _("Increase to make strokes rugged and trembling"),
4957                                                               "/tools/calligraphic/tremor", 0.0,
4958                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4959                                                               0.0, 100, 1, 10.0,
4960                                                               labels, values, G_N_ELEMENTS(labels),
4961                                                               sp_ddc_tremor_value_changed, 1, 0);
4963         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4964         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4965         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4966         }
4968         {
4969         /* Wiggle */
4970         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4971         gdouble values[] = {0, 20, 40, 60, 100};
4972         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4973                                                               _("Pen Wiggle"), _("Wiggle:"),
4974                                                               _("Increase to make the pen waver and wiggle"),
4975                                                               "/tools/calligraphic/wiggle", 0.0,
4976                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4977                                                               0.0, 100, 1, 10.0,
4978                                                               labels, values, G_N_ELEMENTS(labels),
4979                                                               sp_ddc_wiggle_value_changed, 1, 0);
4980         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4981         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4982         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4983         }
4985         {
4986         /* Mass */
4987             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4988         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4989         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4990                                                               _("Pen Mass"), _("Mass:"),
4991                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4992                                                               "/tools/calligraphic/mass", 2.0,
4993                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4994                                                               0.0, 100, 1, 10.0,
4995                                                               labels, values, G_N_ELEMENTS(labels),
4996                                                               sp_ddc_mass_value_changed, 1, 0);
4997         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4998         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4999         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5000         }
5003         /* Trace Background button */
5004         {
5005             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5006                                                           _("Trace Background"),
5007                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5008                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5009                                                           Inkscape::ICON_SIZE_DECORATION );
5010             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5011             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5012             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5013             g_object_set_data( holder, "tracebackground", act );
5014         }
5016         /* Use Pressure button */
5017         {
5018             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5019                                                           _("Pressure"),
5020                                                           _("Use the pressure of the input device to alter the width of the pen"),
5021                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5022                                                           Inkscape::ICON_SIZE_DECORATION );
5023             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5024             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5025             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5026             g_object_set_data( holder, "usepressure", act );
5027         }
5029         /* Use Tilt button */
5030         {
5031             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5032                                                           _("Tilt"),
5033                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5034                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5035                                                           Inkscape::ICON_SIZE_DECORATION );
5036             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5037             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5038             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5039             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5040             g_object_set_data( holder, "usetilt", act );
5041         }
5043         /*calligraphic profile */
5044         {
5045             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5046             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5047             ege_select_one_action_set_appearance (act1, "compact");
5048             g_object_set_data (holder, "profile_selector", act1 );
5050             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5052             sp_dcc_build_presets_list (holder);
5054             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5055             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5056         }
5057     }
5061 //########################
5062 //##    Circle / Arc    ##
5063 //########################
5065 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5067     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5068     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5070     if (v1 == 0 && v2 == 0) {
5071         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5072             gtk_action_set_sensitive( ocb, FALSE );
5073             gtk_action_set_sensitive( make_whole, FALSE );
5074         }
5075     } else {
5076         gtk_action_set_sensitive( ocb, TRUE );
5077         gtk_action_set_sensitive( make_whole, TRUE );
5078     }
5081 static void
5082 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5084     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5086     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5087         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5088         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5089     }
5091     // quit if run by the attr_changed listener
5092     if (g_object_get_data( tbl, "freeze" )) {
5093         return;
5094     }
5096     // in turn, prevent listener from responding
5097     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5099     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5101     bool modmade = false;
5102     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5103          items != NULL;
5104          items = items->next)
5105     {
5106         SPItem *item = SP_ITEM(items->data);
5108         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5110             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5111             SPArc *arc = SP_ARC(item);
5113             if (!strcmp(value_name, "start"))
5114                 ge->start = (adj->value * M_PI)/ 180;
5115             else
5116                 ge->end = (adj->value * M_PI)/ 180;
5118             sp_genericellipse_normalize(ge);
5119             ((SPObject *)arc)->updateRepr();
5120             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5122             modmade = true;
5123         }
5124     }
5126     g_free(namespaced_name);
5128     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5130     sp_arctb_sensitivize( tbl, adj->value, other->value );
5132     if (modmade) {
5133         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5134                                    _("Arc: Change start/end"));
5135     }
5137     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5141 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5143     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5146 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5148     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5152 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5154     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5155     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5156         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5157         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5158     }
5160     // quit if run by the attr_changed listener
5161     if (g_object_get_data( tbl, "freeze" )) {
5162         return;
5163     }
5165     // in turn, prevent listener from responding
5166     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5168     bool modmade = false;
5170     if ( ege_select_one_action_get_active(act) != 0 ) {
5171         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5172              items != NULL;
5173              items = items->next)
5174         {
5175             if (SP_IS_ARC((SPItem *) items->data)) {
5176                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5177                 repr->setAttribute("sodipodi:open", "true");
5178                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5179                 modmade = true;
5180             }
5181         }
5182     } else {
5183         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5184              items != NULL;
5185              items = items->next)
5186         {
5187             if (SP_IS_ARC((SPItem *) items->data))    {
5188                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5189                 repr->setAttribute("sodipodi:open", NULL);
5190                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5191                 modmade = true;
5192             }
5193         }
5194     }
5196     if (modmade) {
5197         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5198                                    _("Arc: Change open/closed"));
5199     }
5201     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5204 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5206     GtkAdjustment *adj;
5207     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5208     gtk_adjustment_set_value(adj, 0.0);
5209     gtk_adjustment_value_changed(adj);
5211     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5212     gtk_adjustment_set_value(adj, 0.0);
5213     gtk_adjustment_value_changed(adj);
5215     spinbutton_defocus( GTK_OBJECT(obj) );
5218 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5219                                       gchar const */*old_value*/, gchar const */*new_value*/,
5220                                       bool /*is_interactive*/, gpointer data)
5222     GObject *tbl = G_OBJECT(data);
5224     // quit if run by the _changed callbacks
5225     if (g_object_get_data( tbl, "freeze" )) {
5226         return;
5227     }
5229     // in turn, prevent callbacks from responding
5230     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5232     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5233     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5235     GtkAdjustment *adj1,*adj2;
5236     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5237     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5238     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5239     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5241     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5243     char const *openstr = NULL;
5244     openstr = repr->attribute("sodipodi:open");
5245     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5247     if (openstr) {
5248         ege_select_one_action_set_active( ocb, 1 );
5249     } else {
5250         ege_select_one_action_set_active( ocb, 0 );
5251     }
5253     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5256 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5257     NULL, /* child_added */
5258     NULL, /* child_removed */
5259     arc_tb_event_attr_changed,
5260     NULL, /* content_changed */
5261     NULL  /* order_changed */
5262 };
5265 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5267     int n_selected = 0;
5268     Inkscape::XML::Node *repr = NULL;
5270     purge_repr_listener( tbl, tbl );
5272     for (GSList const *items = selection->itemList();
5273          items != NULL;
5274          items = items->next)
5275     {
5276         if (SP_IS_ARC((SPItem *) items->data)) {
5277             n_selected++;
5278             repr = SP_OBJECT_REPR((SPItem *) items->data);
5279         }
5280     }
5282     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5284     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5285     if (n_selected == 0) {
5286         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5287     } else if (n_selected == 1) {
5288         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5289         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5291         if (repr) {
5292             g_object_set_data( tbl, "repr", repr );
5293             Inkscape::GC::anchor(repr);
5294             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5295             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5296         }
5297     } else {
5298         // FIXME: implement averaging of all parameters for multiple selected
5299         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5300         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5301         sp_arctb_sensitivize( tbl, 1, 0 );
5302     }
5306 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5308     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5310     EgeAdjustmentAction* eact = 0;
5311     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5314     {
5315         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5316         ege_output_action_set_use_markup( act, TRUE );
5317         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5318         g_object_set_data( holder, "mode_action", act );
5319     }
5321     /* Start */
5322     {
5323         eact = create_adjustment_action( "ArcStartAction",
5324                                          _("Start"), _("Start:"),
5325                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5326                                          "/tools/shapes/arc/start", 0.0,
5327                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5328                                          -360.0, 360.0, 1.0, 10.0,
5329                                          0, 0, 0,
5330                                          sp_arctb_start_value_changed);
5331         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5332     }
5334     /* End */
5335     {
5336         eact = create_adjustment_action( "ArcEndAction",
5337                                          _("End"), _("End:"),
5338                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5339                                          "/tools/shapes/arc/end", 0.0,
5340                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5341                                          -360.0, 360.0, 1.0, 10.0,
5342                                          0, 0, 0,
5343                                          sp_arctb_end_value_changed);
5344         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5345     }
5347     /* Segments / Pie checkbox */
5348     {
5349         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5351         GtkTreeIter iter;
5352         gtk_list_store_append( model, &iter );
5353         gtk_list_store_set( model, &iter,
5354                             0, _("Closed arc"),
5355                             1, _("Switch to segment (closed shape with two radii)"),
5356                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5357                             -1 );
5359         gtk_list_store_append( model, &iter );
5360         gtk_list_store_set( model, &iter,
5361                             0, _("Open Arc"),
5362                             1, _("Switch to arc (unclosed shape)"),
5363                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5364                             -1 );
5366         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5367         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5368         g_object_set_data( holder, "open_action", act );
5370         ege_select_one_action_set_appearance( act, "full" );
5371         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5372         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5373         ege_select_one_action_set_icon_column( act, 2 );
5374         ege_select_one_action_set_icon_size( act, secondarySize );
5375         ege_select_one_action_set_tooltip_column( act, 1  );
5377         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5378         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5379         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5380     }
5382     /* Make Whole */
5383     {
5384         InkAction* inky = ink_action_new( "ArcResetAction",
5385                                           _("Make whole"),
5386                                           _("Make the shape a whole ellipse, not arc or segment"),
5387                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5388                                           secondarySize );
5389         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5390         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5391         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5392         g_object_set_data( holder, "make_whole", inky );
5393     }
5395     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5396     // sensitivize make whole and open checkbox
5397     {
5398         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5399         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5400         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5401     }
5404     sigc::connection *connection = new sigc::connection(
5405         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5406         );
5407     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5408     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5414 // toggle button callbacks and updaters
5416 //########################
5417 //##      Dropper       ##
5418 //########################
5420 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5421     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5422     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5423     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5424     if ( set_action ) {
5425         if ( gtk_toggle_action_get_active( act ) ) {
5426             gtk_action_set_sensitive( set_action, TRUE );
5427         } else {
5428             gtk_action_set_sensitive( set_action, FALSE );
5429         }
5430     }
5432     spinbutton_defocus(GTK_OBJECT(tbl));
5435 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5436     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5437     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5438     spinbutton_defocus(GTK_OBJECT(tbl));
5442 /**
5443  * Dropper auxiliary toolbar construction and setup.
5444  *
5445  * TODO: Would like to add swatch of current color.
5446  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5447  *       can drag and drop places. Will provide a nice mixing palette.
5448  */
5449 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5451     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5452     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5454     {
5455         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5456         ege_output_action_set_use_markup( act, TRUE );
5457         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5458     }
5460     {
5461         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5462                                                       _("Pick opacity"),
5463                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5464                                                       NULL,
5465                                                       Inkscape::ICON_SIZE_DECORATION );
5466         g_object_set( act, "short_label", _("Pick"), NULL );
5467         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5468         g_object_set_data( holder, "pick_action", act );
5469         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5470         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5471     }
5473     {
5474         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5475                                                       _("Assign opacity"),
5476                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5477                                                       NULL,
5478                                                       Inkscape::ICON_SIZE_DECORATION );
5479         g_object_set( act, "short_label", _("Assign"), NULL );
5480         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5481         g_object_set_data( holder, "set_action", act );
5482         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5483         // make sure it's disabled if we're not picking alpha
5484         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5485         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5486     }
5490 //########################
5491 //##      LPETool       ##
5492 //########################
5494 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5496 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5497 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5499     using namespace Inkscape::LivePathEffect;
5501     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5502     SPEventContext *ec = desktop->event_context;
5503     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5504         return;
5505     }
5507     // only take action if run by the attr_changed listener
5508     if (!g_object_get_data(tbl, "freeze")) {
5509         // in turn, prevent listener from responding
5510         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5512         gint mode = ege_select_one_action_get_active(act);
5513         EffectType type = lpesubtools[mode].type;
5515         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5516         bool success = lpetool_try_construction(lc, type);
5517         if (success) {
5518             // since the construction was already performed, we set the state back to inactive
5519             ege_select_one_action_set_active(act, 0);
5520             mode = 0;
5521         } else {
5522             // switch to the chosen subtool
5523             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5524         }
5526         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5527             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5528             prefs->setInt( "/tools/lpetool/mode", mode );
5529         }
5531         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5532     }
5535 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5537     SPEventContext *ec = selection->desktop()->event_context;
5538     if (!SP_IS_LPETOOL_CONTEXT(ec))
5539         return;
5541     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5544 void
5545 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5547     using namespace Inkscape::LivePathEffect;
5548     SPEventContext *ec = selection->desktop()->event_context;
5549     if (!SP_IS_LPETOOL_CONTEXT(ec))
5550         return;
5551     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5553     lpetool_delete_measuring_items(lc);
5554     lpetool_create_measuring_items(lc, selection);
5556     // activate line segment combo box if a single item with LPELineSegment is selected
5557     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5558     SPItem *item = selection->singleItem();
5559     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5560         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5561         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5562         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5563             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5564             g_object_set_data(tbl, "currentlpe", lpe);
5565             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5566             gtk_action_set_sensitive(w, TRUE);
5567             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5568         } else {
5569             g_object_set_data(tbl, "currentlpe", NULL);
5570             g_object_set_data(tbl, "currentlpeitem", NULL);
5571             gtk_action_set_sensitive(w, FALSE);
5572         }
5573     } else {
5574         g_object_set_data(tbl, "currentlpe", NULL);
5575         g_object_set_data(tbl, "currentlpeitem", NULL);
5576         gtk_action_set_sensitive(w, FALSE);
5577     }
5580 static void
5581 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5582     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5583     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5585     bool show = gtk_toggle_action_get_active( act );
5586     prefs->setBool("/tools/lpetool/show_bbox",  show);
5588     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5589         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5590         lpetool_context_reset_limiting_bbox(lc);
5591     }
5594 static void
5595 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5596     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5597     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5598         return;
5600     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5601     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5602     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5603         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5604         bool show = gtk_toggle_action_get_active( act );
5605         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5606         lpetool_show_measuring_info(lc, show);
5607         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5608     }
5611 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5612     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5613     SPUnit const *unit = tracker->getActiveUnit();
5614     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5615     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5617     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5618     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5619         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5620         lpetool_delete_measuring_items(lc);
5621         lpetool_create_measuring_items(lc);
5622     }
5625 static void
5626 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5627     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5628     Inkscape::Selection *selection = desktop->selection;
5630     Geom::OptRect bbox = selection->bounds();
5632     if (bbox) {
5633         Geom::Point A(bbox->min());
5634         Geom::Point B(bbox->max());
5636         A *= desktop->doc2dt();
5637         B *= desktop->doc2dt();
5639         // TODO: should we provide a way to store points in prefs?
5640         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5641         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5642         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5643         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5644         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5646         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5647     }
5649     gtk_toggle_action_set_active(act, false);
5652 static void
5653 sp_line_segment_build_list(GObject *tbl)
5655     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5657     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5658     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5659     gtk_list_store_clear (model);
5661     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5662     {
5663         GtkTreeIter iter;
5664         gtk_list_store_append( model, &iter );
5665         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5666         gtk_list_store_append( model, &iter );
5667         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5668         gtk_list_store_append( model, &iter );
5669         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5670         gtk_list_store_append( model, &iter );
5671         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5672     }
5674     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5677 static void
5678 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5679     using namespace Inkscape::LivePathEffect;
5681     // quit if run by the attr_changed listener
5682     if (g_object_get_data(tbl, "freeze")) {
5683         return;
5684     }
5686     // in turn, prevent listener from responding
5687     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5689     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5690     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5691     if (lpeitem) {
5692         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5693         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5694         sp_lpe_item_update_patheffect(lpeitem, true, true);
5695     }
5697     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5700 static void
5701 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5702     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5704     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5705         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5706     }
5707     gtk_toggle_action_set_active(act, false);
5710 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5712     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5713     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5714     g_object_set_data(holder, "tracker", tracker);
5715     SPUnit const *unit = tracker->getActiveUnit();
5717     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5718     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5720     /** Automatically create a list of LPEs that get added to the toolbar **/
5721     {
5722         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5724         GtkTreeIter iter;
5726         // the first toggle button represents the state that no subtool is active (remove this when
5727         // this can be modeled by EgeSelectOneAction or some other action)
5728         gtk_list_store_append( model, &iter );
5729         gtk_list_store_set( model, &iter,
5730                             0, _("All inactive"),
5731                             1, _("No geometric tool is active"),
5732                             2, "draw-geometry-inactive",
5733                             -1 );
5735         Inkscape::LivePathEffect::EffectType type;
5736         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5737             type =  lpesubtools[i].type;
5738             gtk_list_store_append( model, &iter );
5739             gtk_list_store_set( model, &iter,
5740                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5741                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5742                                 2, lpesubtools[i].icon_name,
5743                                 -1 );
5744         }
5746         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5747         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5748         g_object_set_data( holder, "lpetool_mode_action", act );
5750         ege_select_one_action_set_appearance( act, "full" );
5751         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5752         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5753         ege_select_one_action_set_icon_column( act, 2 );
5754         ege_select_one_action_set_tooltip_column( act, 1  );
5756         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5757         ege_select_one_action_set_active( act, lpeToolMode );
5758         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5759     }
5761     /* Show limiting bounding box */
5762     {
5763         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5764                                                       _("Show limiting bounding box"),
5765                                                       _("Show bounding box (used to cut infinite lines)"),
5766                                                       "show-bounding-box",
5767                                                       Inkscape::ICON_SIZE_DECORATION );
5768         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5769         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5770         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5771     }
5773     /* Set limiting bounding box to bbox of current selection */
5774     {
5775         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5776                                                       _("Get limiting bounding box from selection"),
5777                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5778                                                       "draw-geometry-set-bounding-box",
5779                                                       Inkscape::ICON_SIZE_DECORATION );
5780         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5781         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5782         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5783     }
5786     /* Combo box to choose line segment type */
5787     {
5788         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5789         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5790         ege_select_one_action_set_appearance (act, "compact");
5791         g_object_set_data (holder, "lpetool_line_segment_action", act );
5793         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5795         sp_line_segment_build_list (holder);
5797         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5798         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5799         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5800     }
5802     /* Display measuring info for selected items */
5803     {
5804         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5805                                                       _("Display measuring info"),
5806                                                       _("Display measuring info for selected items"),
5807                                                       "draw-geometry-show-measuring-info",
5808                                                       Inkscape::ICON_SIZE_DECORATION );
5809         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5810         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5811         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5812     }
5814     // add the units menu
5815     {
5816         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5817         gtk_action_group_add_action( mainActions, act );
5818         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5819         g_object_set_data(holder, "lpetool_units_action", act);
5820         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5821     }
5823     /* Open LPE dialog (to adapt parameters numerically) */
5824     {
5825         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5826                                                       _("Open LPE dialog"),
5827                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5828                                                       "dialog-geometry",
5829                                                       Inkscape::ICON_SIZE_DECORATION );
5830         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5831         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5832         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5833     }
5835     //watch selection
5836     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5838     sigc::connection *c_selection_modified =
5839         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5840                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5841     pool->add_connection ("selection-modified", c_selection_modified);
5843     sigc::connection *c_selection_changed =
5844         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5845                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5846     pool->add_connection ("selection-changed", c_selection_changed);
5849 //########################
5850 //##       Eraser       ##
5851 //########################
5853 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5855     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5856     prefs->setDouble( "/tools/eraser/width", adj->value );
5857     update_presets_list(tbl);
5860 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5862     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5863     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5864     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5865         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5866         prefs->setBool( "/tools/eraser/mode", eraserMode );
5867     }
5869     // only take action if run by the attr_changed listener
5870     if (!g_object_get_data( tbl, "freeze" )) {
5871         // in turn, prevent listener from responding
5872         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5874         if ( eraserMode != 0 ) {
5875         } else {
5876         }
5877         // TODO finish implementation
5879         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5880     }
5883 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5885     {
5886         /* Width */
5887         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5888         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5889         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5890                                                               _("Pen Width"), _("Width:"),
5891                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5892                                                               "/tools/eraser/width", 15,
5893                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5894                                                               1, 100, 1.0, 10.0,
5895                                                               labels, values, G_N_ELEMENTS(labels),
5896                                                               sp_erc_width_value_changed, 1, 0);
5897         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5898         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5899         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5900     }
5902     {
5903         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5905         GtkTreeIter iter;
5906         gtk_list_store_append( model, &iter );
5907         gtk_list_store_set( model, &iter,
5908                             0, _("Delete"),
5909                             1, _("Delete objects touched by the eraser"),
5910                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5911                             -1 );
5913         gtk_list_store_append( model, &iter );
5914         gtk_list_store_set( model, &iter,
5915                             0, _("Cut"),
5916                             1, _("Cut out from objects"),
5917                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5918                             -1 );
5920         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5921         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5922         g_object_set_data( holder, "eraser_mode_action", act );
5924         ege_select_one_action_set_appearance( act, "full" );
5925         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5926         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5927         ege_select_one_action_set_icon_column( act, 2 );
5928         ege_select_one_action_set_tooltip_column( act, 1  );
5930         /// @todo Convert to boolean?
5931         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5932         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5933         ege_select_one_action_set_active( act, eraserMode );
5934         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5935     }
5939 //########################
5940 //##    Text Toolbox    ##
5941 //########################
5942 /*
5943 static void
5944 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5946     //Call back for letter sizing spinbutton
5949 static void
5950 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5952     //Call back for line height spinbutton
5955 static void
5956 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5958     //Call back for horizontal kerning spinbutton
5961 static void
5962 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5964     //Call back for vertical kerning spinbutton
5967 static void
5968 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5970     //Call back for letter rotation spinbutton
5971 }*/
5973 namespace {
5975 void
5976 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5978     // quit if run by the _changed callbacks
5979     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5980         return;
5981     }
5983     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5985     SPStyle *query =
5986         sp_style_new (SP_ACTIVE_DOCUMENT);
5988     int result_family =
5989         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5991     int result_style =
5992         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5994     int result_numbers =
5995         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5997     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5999     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6000     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6001         // there are no texts in selection, read from prefs
6003         sp_style_read_from_prefs(query, "/tools/text");
6005         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6006             // do not reset the toolbar style from prefs if we already did it last time
6007             sp_style_unref(query);
6008             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6009             return;
6010         }
6011         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6012     } else {
6013         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6014     }
6016     if (query->text)
6017     {
6018         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6019             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6020             gtk_entry_set_text (GTK_ENTRY (entry), "");
6022         } else if (query->text->font_specification.value || query->text->font_family.value) {
6024             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6025             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6027             // Get the font that corresponds
6028             Glib::ustring familyName;
6030             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6031             if (font) {
6032                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6033                 font->Unref();
6034                 font = NULL;
6035             }
6037             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6039             Gtk::TreeIter iter;
6040             try {
6041                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6042                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6043                 iter = model->get_iter(path);
6044             } catch (...) {
6045                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6046                 sp_style_unref(query);
6047                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6048                 return;
6049             }
6051             combo->set_active (iter);
6052         }
6054         //Size
6055         {
6056             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6057             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6058             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6059             g_free(str);
6060         }
6062         //Anchor
6063         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6064         {
6065             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6066             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6067             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6068             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6069         }
6070         else
6071         {
6072             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6073             {
6074                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6075                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6076                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6077                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6078             }
6079             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6080             {
6081                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6082                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6083                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6084                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6085             }
6086             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6087             {
6088                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6089                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6090                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6091                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6092             }
6093         }
6095         //Style
6096         {
6097             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6099             gboolean active = gtk_toggle_button_get_active (button);
6100             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));
6102             if (active != check)
6103             {
6104                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6105                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6106                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6107             }
6108         }
6110         {
6111             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6113             gboolean active = gtk_toggle_button_get_active (button);
6114             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6116             if (active != check)
6117             {
6118                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6119                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6120                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6121             }
6122         }
6124         //Orientation
6125         //locking both buttons, changing one affect all group (both)
6126         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6127         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6129         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6130         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6132         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6133         {
6134             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6135         }
6136         else
6137         {
6138             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6139         }
6140         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6141         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6142     }
6144     sp_style_unref(query);
6146     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6149 void
6150 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6152     sp_text_toolbox_selection_changed (selection, tbl);
6155 void
6156 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6158     sp_text_toolbox_selection_changed (NULL, tbl);
6161 void
6162 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6163                                 GObject             *tbl)
6165     // quit if run by the _changed callbacks
6166     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6167         return;
6168     }
6170     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6172     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6173     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6174     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6176     //g_print ("family changed to: %s\n", family);
6178     SPStyle *query =
6179         sp_style_new (SP_ACTIVE_DOCUMENT);
6181     int result_fontspec =
6182         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6184     SPCSSAttr *css = sp_repr_css_attr_new ();
6186     // First try to get the font spec from the stored value
6187     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6189     if (fontSpec.empty()) {
6190         // Construct a new font specification if it does not yet exist
6191         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6192         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6193         fontFromStyle->Unref();
6194     }
6196     if (!fontSpec.empty()) {
6198         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6200         if (!newFontSpec.empty()) {
6202             if (fontSpec != newFontSpec) {
6204                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6206                 if (font) {
6207                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6209                     // Set all the these just in case they were altered when finding the best
6210                     // match for the new family and old style...
6212                     gchar c[256];
6214                     font->Family(c, 256);
6216                     sp_repr_css_set_property (css, "font-family", c);
6218                     font->Attribute( "weight", c, 256);
6219                     sp_repr_css_set_property (css, "font-weight", c);
6221                     font->Attribute("style", c, 256);
6222                     sp_repr_css_set_property (css, "font-style", c);
6224                     font->Attribute("stretch", c, 256);
6225                     sp_repr_css_set_property (css, "font-stretch", c);
6227                     font->Attribute("variant", c, 256);
6228                     sp_repr_css_set_property (css, "font-variant", c);
6230                     font->Unref();
6231                 }
6232             }
6234         } else {
6235             // If the old font on selection (or default) was not existing on the system,
6236             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6237             // setting the family reported by the family chooser.
6239             //g_print ("fallback setting family: %s\n", family);
6240             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6241             sp_repr_css_set_property (css, "font-family", family);
6242         }
6243     }
6245     // If querying returned nothing, set the default style of the tool (for new texts)
6246     if (result_fontspec == QUERY_STYLE_NOTHING)
6247     {
6248         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6249         prefs->mergeStyle("/tools/text/style", css);
6250         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6251     }
6252     else
6253     {
6254         sp_desktop_set_style (desktop, css, true, true);
6255     }
6257     sp_style_unref(query);
6259     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6260                                    _("Text: Change font family"));
6261     sp_repr_css_attr_unref (css);
6263     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6265     // unfreeze
6266     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6268     // focus to canvas
6269     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6273 void
6274 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6275                                    gpointer          data)
6277     if (g_object_get_data (G_OBJECT (button), "block")) return;
6278     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6279     int prop = GPOINTER_TO_INT(data);
6281     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6283     // move the x of all texts to preserve the same bbox
6284     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6285     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6286         if (SP_IS_TEXT((SPItem *) items->data)) {
6287             SPItem *item = SP_ITEM(items->data);
6289             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6290             // below, variable names suggest horizontal move, but we check the writing direction
6291             // and move in the corresponding axis
6292             int axis;
6293             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6294                 axis = NR::X;
6295             } else {
6296                 axis = NR::Y;
6297             }
6299             Geom::OptRect bbox
6300                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6301             if (!bbox)
6302                 continue;
6303             double width = bbox->dimensions()[axis];
6304             // If you want to align within some frame, other than the text's own bbox, calculate
6305             // the left and right (or top and bottom for tb text) slacks of the text inside that
6306             // frame (currently unused)
6307             double left_slack = 0;
6308             double right_slack = 0;
6309             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6310             double move = 0;
6311             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6312                 switch (prop) {
6313                     case 0:
6314                         move = -left_slack;
6315                         break;
6316                     case 1:
6317                         move = width/2 + (right_slack - left_slack)/2;
6318                         break;
6319                     case 2:
6320                         move = width + right_slack;
6321                         break;
6322                 }
6323             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6324                 switch (prop) {
6325                     case 0:
6326                         move = -width/2 - left_slack;
6327                         break;
6328                     case 1:
6329                         move = (right_slack - left_slack)/2;
6330                         break;
6331                     case 2:
6332                         move = width/2 + right_slack;
6333                         break;
6334                 }
6335             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6336                 switch (prop) {
6337                     case 0:
6338                         move = -width - left_slack;
6339                         break;
6340                     case 1:
6341                         move = -width/2 + (right_slack - left_slack)/2;
6342                         break;
6343                     case 2:
6344                         move = right_slack;
6345                         break;
6346                 }
6347             }
6348             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6349             if (axis == NR::X) {
6350                 XY = XY + Geom::Point (move, 0);
6351             } else {
6352                 XY = XY + Geom::Point (0, move);
6353             }
6354             SP_TEXT(item)->attributes.setFirstXY(XY);
6355             SP_OBJECT(item)->updateRepr();
6356             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6357         }
6358     }
6360     SPCSSAttr *css = sp_repr_css_attr_new ();
6361     switch (prop)
6362     {
6363         case 0:
6364         {
6365             sp_repr_css_set_property (css, "text-anchor", "start");
6366             sp_repr_css_set_property (css, "text-align", "start");
6367             break;
6368         }
6369         case 1:
6370         {
6371             sp_repr_css_set_property (css, "text-anchor", "middle");
6372             sp_repr_css_set_property (css, "text-align", "center");
6373             break;
6374         }
6376         case 2:
6377         {
6378             sp_repr_css_set_property (css, "text-anchor", "end");
6379             sp_repr_css_set_property (css, "text-align", "end");
6380             break;
6381         }
6383         case 3:
6384         {
6385             sp_repr_css_set_property (css, "text-anchor", "start");
6386             sp_repr_css_set_property (css, "text-align", "justify");
6387             break;
6388         }
6389     }
6391     SPStyle *query =
6392         sp_style_new (SP_ACTIVE_DOCUMENT);
6393     int result_numbers =
6394         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6396     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6397     if (result_numbers == QUERY_STYLE_NOTHING)
6398     {
6399         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6400         prefs->mergeStyle("/tools/text/style", css);
6401     }
6403     sp_style_unref(query);
6405     sp_desktop_set_style (desktop, css, true, true);
6406     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6407                                    _("Text: Change alignment"));
6408     sp_repr_css_attr_unref (css);
6410     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6413 void
6414 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6415                                gpointer          data)
6417     if (g_object_get_data (G_OBJECT (button), "block")) return;
6419     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6420     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6421     int          prop       = GPOINTER_TO_INT(data);
6422     bool         active     = gtk_toggle_button_get_active (button);
6424     SPStyle *query =
6425         sp_style_new (SP_ACTIVE_DOCUMENT);
6427     int result_fontspec =
6428         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6430     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6431     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6432     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6434     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6435     Glib::ustring newFontSpec = "";
6437     if (fontSpec.empty()) {
6438         // Construct a new font specification if it does not yet exist
6439         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6440         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6441         fontFromStyle->Unref();
6442     }
6444     bool nochange = true;
6445     switch (prop)
6446     {
6447         case 0:
6448         {
6449             if (!fontSpec.empty()) {
6450                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6451                 if (!newFontSpec.empty()) {
6452                     // Don't even set the bold if the font didn't exist on the system
6453                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6454                     nochange = false;
6455                 }
6456             }
6457             // set or reset the button according
6458             if(nochange) {
6459                 gboolean check = gtk_toggle_button_get_active (button);
6461                 if (active != check)
6462                 {
6463                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6464                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6465                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6466                 }
6467             }
6469             break;
6470         }
6472         case 1:
6473         {
6474             if (!fontSpec.empty()) {
6475                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6476                 if (!newFontSpec.empty()) {
6477                     // Don't even set the italic if the font didn't exist on the system
6478                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6479                     nochange = false;
6480                 }
6481             }
6482             if(nochange) {
6483                 gboolean check = gtk_toggle_button_get_active (button);
6485                 if (active != check)
6486                 {
6487                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6488                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6489                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6490                 }
6491             }
6492             break;
6493         }
6494     }
6496     if (!newFontSpec.empty()) {
6497         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6498     }
6500     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6501     if (result_fontspec == QUERY_STYLE_NOTHING)
6502     {
6503         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6504         prefs->mergeStyle("/tools/text/style", css);
6505     }
6507     sp_style_unref(query);
6509     sp_desktop_set_style (desktop, css, true, true);
6510     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6511                                    _("Text: Change font style"));
6512     sp_repr_css_attr_unref (css);
6514     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6517 void
6518 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6519                                      gpointer         data)
6521     if (g_object_get_data (G_OBJECT (button), "block")) {
6522         return;
6523     }
6525     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6526     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6527     int          prop       = GPOINTER_TO_INT(data);
6529     switch (prop)
6530     {
6531         case 0:
6532         {
6533             sp_repr_css_set_property (css, "writing-mode", "lr");
6534             break;
6535         }
6537         case 1:
6538         {
6539             sp_repr_css_set_property (css, "writing-mode", "tb");
6540             break;
6541         }
6542     }
6544     SPStyle *query =
6545         sp_style_new (SP_ACTIVE_DOCUMENT);
6546     int result_numbers =
6547         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6549     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6550     if (result_numbers == QUERY_STYLE_NOTHING)
6551     {
6552         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6553         prefs->mergeStyle("/tools/text/style", css);
6554     }
6556     sp_desktop_set_style (desktop, css, true, true);
6557     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6558                                    _("Text: Change orientation"));
6559     sp_repr_css_attr_unref (css);
6561     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6564 gboolean
6565 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6567     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6568     if (!desktop) return FALSE;
6570     switch (get_group0_keyval (event)) {
6571         case GDK_KP_Enter: // chosen
6572         case GDK_Return:
6573             // unfreeze and update, which will defocus
6574             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6575             sp_text_toolbox_family_changed (NULL, tbl);
6576             return TRUE; // I consumed the event
6577             break;
6578         case GDK_Escape:
6579             // defocus
6580             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6581             return TRUE; // I consumed the event
6582             break;
6583     }
6584     return FALSE;
6587 gboolean
6588 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6590     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6591     if (!desktop) return FALSE;
6593     switch (get_group0_keyval (event)) {
6594         case GDK_KP_Enter:
6595         case GDK_Return:
6596         case GDK_Escape: // defocus
6597             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6598             return TRUE; // I consumed the event
6599             break;
6600         case GDK_w:
6601         case GDK_W:
6602             if (event->state & GDK_CONTROL_MASK) {
6603                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6604                 return TRUE; // I consumed the event
6605             }
6606             break;
6607     }
6608     return FALSE;
6612 void
6613 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6614                                GObject     *tbl)
6616      // quit if run by the _changed callbacks
6617     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6618         return;
6619     }
6621     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6623    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6625     // If this is not from selecting a size in the list (in which case get_active will give the
6626     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6627     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6628     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6629    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6630         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6631         return;
6632    }
6634     gdouble value = -1;
6635     {
6636         gchar *endptr;
6637         gchar *const text = gtk_combo_box_get_active_text(cbox);
6638         if (text) {
6639             value = g_strtod(text, &endptr);
6640             if (endptr == text) {  // Conversion failed, non-numeric input.
6641                 value = -1;
6642             }
6643             g_free(text);
6644         }
6645     }
6646     if (value <= 0) {
6647         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6648         return; // could not parse value
6649     }
6651     SPCSSAttr *css = sp_repr_css_attr_new ();
6652     Inkscape::CSSOStringStream osfs;
6653     osfs << value;
6654     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6656     SPStyle *query =
6657         sp_style_new (SP_ACTIVE_DOCUMENT);
6658     int result_numbers =
6659         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6661     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6662     if (result_numbers == QUERY_STYLE_NOTHING)
6663     {
6664         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6665         prefs->mergeStyle("/tools/text/style", css);
6666     }
6668     sp_style_unref(query);
6670     sp_desktop_set_style (desktop, css, true, true);
6671     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6672                                    _("Text: Change font size"));
6673     sp_repr_css_attr_unref (css);
6675     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6677     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6680 gboolean
6681 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6683     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6684     if (!desktop) return FALSE;
6686     if (!g_object_get_data (tbl, "esc-pressed")) {
6687         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6688         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6689         sp_text_toolbox_size_changed (cbox, tbl);
6690         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6691     }
6692     return FALSE; // I consumed the event
6696 gboolean
6697 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6699     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6700     if (!desktop) return FALSE;
6702     switch (get_group0_keyval (event)) {
6703         case GDK_Escape: // defocus
6704             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6705             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6706             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6707             return TRUE; // I consumed the event
6708             break;
6709         case GDK_Return: // defocus
6710         case GDK_KP_Enter:
6711             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6712             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6713             sp_text_toolbox_size_changed (cbox, tbl);
6714             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6715             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6716             return TRUE; // I consumed the event
6717             break;
6718     }
6719     return FALSE;
6722 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6723 // does not work!
6724 gboolean
6725 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6726                                  GdkEventFocus    */*event*/,
6727                                  GObject          *tbl)
6729     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6730     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6731     return FALSE;
6734 gboolean
6735 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6736                                  GdkEventFocus    */*event*/,
6737                                  GObject          *tbl)
6739     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6740     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6741     return FALSE;
6744 void
6745 cell_data_func  (GtkCellLayout */*cell_layout*/,
6746                  GtkCellRenderer   *cell,
6747                  GtkTreeModel      *tree_model,
6748                  GtkTreeIter       *iter,
6749                  gpointer           /*data*/)
6751     gchar *family;
6752     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6753     gchar *const family_escaped = g_markup_escape_text(family, -1);
6755     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6756     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6757     if (show_sample) {
6759         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6760         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6762     std::stringstream markup;
6763     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6764            << family_escaped << "'>" << sample_escaped << "</span>";
6765     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6767         g_free(sample_escaped);
6768     } else {
6769         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6770     }
6772     g_free(family);
6773     g_free(family_escaped);
6776 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6777                                                 GtkTreeModel       *model,
6778                                                 GtkTreeIter        *iter,
6779                                                 GObject            *tbl)
6781     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6782     // press Enter again after choosing a completion)
6783     gchar *family = 0;
6784     gtk_tree_model_get(model, iter, 0, &family, -1);
6786     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6787     gtk_entry_set_text (GTK_ENTRY (entry), family);
6789     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6790     sp_text_toolbox_family_changed (NULL, tbl);
6791     return TRUE;
6795 static void
6796 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6797     GtkEntry *entry;
6798     GtkEntryCompletion *completion;
6799     GtkTreeModel *model;
6801     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6802     completion = gtk_entry_completion_new();
6803     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6804     gtk_entry_completion_set_model(completion, model);
6805     gtk_entry_completion_set_text_column(completion, 0);
6806     gtk_entry_completion_set_inline_completion(completion, FALSE);
6807     gtk_entry_completion_set_inline_selection(completion, FALSE);
6808     gtk_entry_completion_set_popup_completion(completion, TRUE);
6809     gtk_entry_set_completion(entry, completion);
6811     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6813     g_object_unref(completion);
6816 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6817                                       void */*property*/,
6818                                       GObject *tbl)
6820   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6822   gboolean shown;
6823   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6824   if (shown) {
6825          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6826          //g_print("POP: notify: SHOWN\n");
6827   } else {
6828          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6830          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6831          // find out if the drop down was closed by Enter and if so, manually update (only
6832          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6833          GdkEvent *ev = gtk_get_current_event();
6834          if (ev) {
6835              //g_print ("ev type: %d\n", ev->type);
6836              if (ev->type == GDK_KEY_PRESS) {
6837                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6838                      case GDK_KP_Enter: // chosen
6839                      case GDK_Return:
6840                      {
6841                          // make sure the chosen one is inserted into the entry
6842                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6843                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6844                          GtkTreeIter iter;
6845                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6846                          if (has_active) {
6847                              gchar *family;
6848                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6849                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6850                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6851                          }
6853                          // update
6854                          sp_text_toolbox_family_changed (NULL, tbl);
6855                          break;
6856                      }
6857                  }
6858              }
6859          }
6861          // regardless of whether we updated, defocus the widget
6862          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6863          if (desktop)
6864              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6865          //g_print("POP: notify: HIDDEN\n");
6866   }
6869 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6871     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6872     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6874     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6875     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6877     GtkTooltips *tt = gtk_tooltips_new();
6879     ////////////Family
6880     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6881     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6883     gtk_rc_parse_string (
6884        "style \"dropdown-as-list-style\"\n"
6885        "{\n"
6886        "    GtkComboBox::appears-as-list = 1\n"
6887        "}\n"
6888        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6889     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6890     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6892     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6894     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6896     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6897     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6899     // expand the field a bit so as to view more of the previews in the drop-down
6900     GtkRequisition req;
6901     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6902     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6904     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6905     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6907     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6908     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6909              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6910     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6911     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6912     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6914     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6915     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6917     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6918     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6919     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6920     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6922     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6923     GtkWidget *box = gtk_event_box_new ();
6924     gtk_container_add (GTK_CONTAINER (box), image);
6925     gtk_toolbar_append_widget( tbl, box, "", "");
6926     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6927     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6928     gtk_widget_hide (GTK_WIDGET (box));
6929     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6931     ////////////Size
6932     gchar const *const sizes[] = {
6933         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6934         "16", "18", "20", "22", "24", "28",
6935         "32", "36", "40", "48", "56", "64", "72", "144"
6936     };
6938     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6939     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6940         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6941     }
6942     gtk_widget_set_size_request (cbox, 80, -1);
6943     gtk_toolbar_append_widget( tbl, cbox, "", "");
6944     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6945     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6946     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6947     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6949     ////////////Text anchor
6950     GtkWidget *group   = gtk_radio_button_new (NULL);
6951     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6952     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6954     // left
6955     GtkWidget *rbutton = group;
6956     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6957     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6958     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6960     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6961     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6962     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6963     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6965     // center
6966     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6967     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6968     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6969     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6971     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6972     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6973     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6974     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6976     // right
6977     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6978     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6979     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6980     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6982     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6983     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6984     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6985     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6987     // fill
6988     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6989     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6990     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6991     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6993     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6994     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6995     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6996     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6998     gtk_toolbar_append_widget( tbl, row, "", "");
7000     //spacer
7001     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7003     ////////////Text style
7004     row = gtk_hbox_new (FALSE, 4);
7006     // bold
7007     rbutton = gtk_toggle_button_new ();
7008     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7009     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7010     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7011     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7013     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7014     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7015     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7017     // italic
7018     rbutton = gtk_toggle_button_new ();
7019     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7020     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7021     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7022     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7024     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7025     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7026     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7028     gtk_toolbar_append_widget( tbl, row, "", "");
7030     //spacer
7031     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7033     // Text orientation
7034     group   = gtk_radio_button_new (NULL);
7035     row     = gtk_hbox_new (FALSE, 4);
7036     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7038     // horizontal
7039     rbutton = group;
7040     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7041     gtk_container_add           (GTK_CONTAINER (rbutton),
7042                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7043     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7044     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7046     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7047     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7048     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7050     // vertical
7051     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7052     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7053     gtk_container_add           (GTK_CONTAINER (rbutton),
7054                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7055     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7056     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7058     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7059     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7060     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7061     gtk_toolbar_append_widget( tbl, row, "", "" );
7064     //watch selection
7065     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7067     sigc::connection *c_selection_changed =
7068         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7069                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7070     pool->add_connection ("selection-changed", c_selection_changed);
7072     sigc::connection *c_selection_modified =
7073         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7074                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7075     pool->add_connection ("selection-modified", c_selection_modified);
7077     sigc::connection *c_subselection_changed =
7078         new sigc::connection (desktop->connectToolSubselectionChanged
7079                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7080     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7082     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7085     gtk_widget_show_all( GTK_WIDGET(tbl) );
7087     return GTK_WIDGET(tbl);
7088 } // end of sp_text_toolbox_new()
7090 }//<unnamed> namespace
7093 //#########################
7094 //##      Connector      ##
7095 //#########################
7097 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7099     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7100     prefs->setBool("/tools/connector/mode",
7101                 gtk_toggle_action_get_active( act ));
7104 static void sp_connector_path_set_avoid(void)
7106     cc_selection_set_avoid(true);
7110 static void sp_connector_path_set_ignore(void)
7112     cc_selection_set_avoid(false);
7115 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7117     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7118     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7119     SPDocument *doc = sp_desktop_document(desktop);
7121     if (!sp_document_get_undo_sensitive(doc))
7122     {
7123         return;
7124     }
7127     // quit if run by the _changed callbacks
7128     if (g_object_get_data( tbl, "freeze" )) {
7129         return;
7130     }
7132     // in turn, prevent callbacks from responding
7133     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7135     bool is_orthog = gtk_toggle_action_get_active( act );
7136     gchar orthog_str[] = "orthogonal";
7137     gchar polyline_str[] = "polyline";
7138     gchar *value = is_orthog ? orthog_str : polyline_str ;
7140     bool modmade = false;
7141     GSList *l = (GSList *) selection->itemList();
7142     while (l) {
7143         SPItem *item = (SPItem *) l->data;
7145         if (cc_item_is_connector(item)) {
7146             sp_object_setAttribute(item, "inkscape:connector-type",
7147                     value, false);
7148             item->avoidRef->handleSettingChange();
7149             modmade = true;
7150         }
7151         l = l->next;
7152     }
7154     if (!modmade)
7155     {
7156         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7157         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7158     }
7160     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7161             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7163     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7166 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7168     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7169     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7170     SPDocument *doc = sp_desktop_document(desktop);
7172     if (!sp_document_get_undo_sensitive(doc))
7173     {
7174         return;
7175     }
7178     // quit if run by the _changed callbacks
7179     if (g_object_get_data( tbl, "freeze" )) {
7180         return;
7181     }
7183     // in turn, prevent callbacks from responding
7184     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7186     gdouble newValue = gtk_adjustment_get_value(adj);
7187     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7188     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7190     bool modmade = false;
7191     GSList *l = (GSList *) selection->itemList();
7192     while (l) {
7193         SPItem *item = (SPItem *) l->data;
7195         if (cc_item_is_connector(item)) {
7196             sp_object_setAttribute(item, "inkscape:connector-curvature",
7197                     value, false);
7198             item->avoidRef->handleSettingChange();
7199             modmade = true;
7200         }
7201         l = l->next;
7202     }
7204     if (!modmade)
7205     {
7206         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7207         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7208     }
7210     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7211             _("Change connector curvature"));
7213     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7217 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7219     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7220     SPDocument *doc = sp_desktop_document(desktop);
7222     if (!sp_document_get_undo_sensitive(doc))
7223     {
7224         return;
7225     }
7227     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7229     if ( !repr->attribute("inkscape:connector-spacing") &&
7230             ( adj->value == defaultConnSpacing )) {
7231         // Don't need to update the repr if the attribute doesn't
7232         // exist and it is being set to the default value -- as will
7233         // happen at startup.
7234         return;
7235     }
7237     // quit if run by the attr_changed listener
7238     if (g_object_get_data( tbl, "freeze" )) {
7239         return;
7240     }
7242     // in turn, prevent listener from responding
7243     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7245     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7246     SP_OBJECT(desktop->namedview)->updateRepr();
7248     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7249     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7250         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7251         Geom::Matrix m = Geom::identity();
7252         avoid_item_move(&m, item);
7253     }
7255     if (items) {
7256         g_slist_free(items);
7257     }
7259     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7260             _("Change connector spacing"));
7262     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7265 static void sp_connector_graph_layout(void)
7267     if (!SP_ACTIVE_DESKTOP) return;
7268     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7270     // hack for clones, see comment in align-and-distribute.cpp
7271     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7272     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7274     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7276     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7278     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7281 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7283     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7284     prefs->setBool("/tools/connector/directedlayout",
7285                 gtk_toggle_action_get_active( act ));
7288 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7290     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7291     prefs->setBool("/tools/connector/avoidoverlaplayout",
7292                 gtk_toggle_action_get_active( act ));
7296 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7298     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7299     prefs->setDouble("/tools/connector/length", adj->value);
7302 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7303                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7304                                             bool /*is_interactive*/, gpointer data)
7306     GtkWidget *tbl = GTK_WIDGET(data);
7308     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7309         return;
7310     }
7311     if (strcmp(name, "inkscape:connector-spacing") == 0)
7312     {
7313         GtkAdjustment *adj = (GtkAdjustment*)
7314                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7315         gdouble spacing = defaultConnSpacing;
7316         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7318         gtk_adjustment_set_value(adj, spacing);
7319         gtk_adjustment_value_changed(adj);
7320     }
7322     spinbutton_defocus(GTK_OBJECT(tbl));
7325 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7327     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7328     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7330     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7331         cc_create_connection_point(cc);
7334 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7336     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7337     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7339     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7340         cc_remove_connection_point(cc);
7343 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7344     NULL, /* child_added */
7345     NULL, /* child_removed */
7346     connector_tb_event_attr_changed,
7347     NULL, /* content_changed */
7348     NULL  /* order_changed */
7349 };
7351 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7353     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7354     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7355     SPItem *item = selection->singleItem();
7356     if (SP_IS_PATH(item))
7357     {
7358         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7359         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7360         gtk_toggle_action_set_active(act, is_orthog);
7361         gtk_adjustment_set_value(adj, curvature);
7362     }
7366 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7368     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7369     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7371     // Editing mode toggle button
7372     {
7373         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7374                                                       _("EditMode"),
7375                                                       _("Switch between connection point editing and connector drawing mode"),
7376                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7377                                                       Inkscape::ICON_SIZE_DECORATION );
7378         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7380         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7381         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7382         g_object_set_data( holder, "mode", act );
7383         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7384     }
7387     {
7388         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7389                                           _("Avoid"),
7390                                           _("Make connectors avoid selected objects"),
7391                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7392                                           secondarySize );
7393         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7394         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7395     }
7397     {
7398         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7399                                           _("Ignore"),
7400                                           _("Make connectors ignore selected objects"),
7401                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7402                                           secondarySize );
7403         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7404         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7405     }
7407     // Orthogonal connectors toggle button
7408     {
7409         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7410                                                       _("Orthogonal"),
7411                                                       _("Make connector orthogonal or polyline"),
7412                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7413                                                       Inkscape::ICON_SIZE_DECORATION );
7414         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7416         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7417         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7418         g_object_set_data( holder, "orthogonal", act );
7419         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7420     }
7422     EgeAdjustmentAction* eact = 0;
7423     // Curvature spinbox
7424     eact = create_adjustment_action( "ConnectorCurvatureAction",
7425                                     _("Connector Curvature"), _("Curvature:"),
7426                                     _("The amount of connectors curvature"),
7427                                     "/tools/connector/curvature", defaultConnCurvature,
7428                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7429                                     0, 100, 1.0, 10.0,
7430                                     0, 0, 0,
7431                                     connector_curvature_changed, 1, 0 );
7432     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7434     // Spacing spinbox
7435     eact = create_adjustment_action( "ConnectorSpacingAction",
7436                                     _("Connector Spacing"), _("Spacing:"),
7437                                     _("The amount of space left around objects by auto-routing connectors"),
7438                                     "/tools/connector/spacing", defaultConnSpacing,
7439                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7440                                     0, 100, 1.0, 10.0,
7441                                     0, 0, 0,
7442                                     connector_spacing_changed, 1, 0 );
7443     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7445     // Graph (connector network) layout
7446     {
7447         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7448                                           _("Graph"),
7449                                           _("Nicely arrange selected connector network"),
7450                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7451                                           secondarySize );
7452         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7453         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7454     }
7456     // Default connector length spinbox
7457     eact = create_adjustment_action( "ConnectorLengthAction",
7458                                      _("Connector Length"), _("Length:"),
7459                                      _("Ideal length for connectors when layout is applied"),
7460                                      "/tools/connector/length", 100,
7461                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7462                                      10, 1000, 10.0, 100.0,
7463                                      0, 0, 0,
7464                                      connector_length_changed, 1, 0 );
7465     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7468     // Directed edges toggle button
7469     {
7470         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7471                                                       _("Downwards"),
7472                                                       _("Make connectors with end-markers (arrows) point downwards"),
7473                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7474                                                       Inkscape::ICON_SIZE_DECORATION );
7475         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7477         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7478         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7480         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7481         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7482         );
7483     }
7485     // Avoid overlaps toggle button
7486     {
7487         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7488                                                       _("Remove overlaps"),
7489                                                       _("Do not allow overlapping shapes"),
7490                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7491                                                       Inkscape::ICON_SIZE_DECORATION );
7492         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7494         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7495         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7497         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7498     }
7501     // New connection point button
7502     {
7503         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7504                                           _("New connection point"),
7505                                           _("Add a new connection point to the currently selected item"),
7506                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7507                                           secondarySize );
7508         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7509         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7510     }
7512     // Remove selected connection point button
7514     {
7515         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7516                                           _("Remove connection point"),
7517                                           _("Remove the currently selected connection point"),
7518                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7519                                           secondarySize );
7520         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7521         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7522     }
7525     // Code to watch for changes to the connector-spacing attribute in
7526     // the XML.
7527     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7528     g_assert(repr != NULL);
7530     purge_repr_listener( holder, holder );
7532     if (repr) {
7533         g_object_set_data( holder, "repr", repr );
7534         Inkscape::GC::anchor(repr);
7535         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7536         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7537     }
7538 } // end of sp_connector_toolbox_prep()
7541 //#########################
7542 //##     Paintbucket     ##
7543 //#########################
7545 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7547     gint channels = ege_select_one_action_get_active( act );
7548     flood_channels_set_channels( channels );
7551 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7553     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7554     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7557 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7559     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7560     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7563 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7565     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7566     SPUnit const *unit = tracker->getActiveUnit();
7567     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7569     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7570     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7573 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7575     // FIXME: make defaults settable via Inkscape Options
7576     struct KeyValue {
7577         char const *key;
7578         double value;
7579     } const key_values[] = {
7580         {"threshold", 15},
7581         {"offset", 0.0}
7582     };
7584     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7585         KeyValue const &kv = key_values[i];
7586         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7587         if ( adj ) {
7588             gtk_adjustment_set_value(adj, kv.value);
7589         }
7590     }
7592     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7593     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7594     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7595     ege_select_one_action_set_active( autogap_action, 0 );
7598 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7600     EgeAdjustmentAction* eact = 0;
7601     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7603     {
7604         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7606         GList* items = 0;
7607         gint count = 0;
7608         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7609         {
7610             GtkTreeIter iter;
7611             gtk_list_store_append( model, &iter );
7612             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7613             count++;
7614         }
7615         g_list_free( items );
7616         items = 0;
7617         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7618         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7619         ege_select_one_action_set_appearance( act1, "compact" );
7620         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7621         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7622         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7623         g_object_set_data( holder, "channels_action", act1 );
7624     }
7626     // Spacing spinbox
7627     {
7628         eact = create_adjustment_action(
7629             "ThresholdAction",
7630             _("Fill Threshold"), _("Threshold:"),
7631             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7632             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7633             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7634             0, 0, 0,
7635             paintbucket_threshold_changed, 1, 0 );
7637         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7638         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7639     }
7641     // Create the units menu.
7642     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7643     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7644     if (!stored_unit.empty())
7645         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7646     g_object_set_data( holder, "tracker", tracker );
7647     {
7648         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7649         gtk_action_group_add_action( mainActions, act );
7650     }
7652     // Offset spinbox
7653     {
7654         eact = create_adjustment_action(
7655             "OffsetAction",
7656             _("Grow/shrink by"), _("Grow/shrink by:"),
7657             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7658             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7659             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7660             0, 0, 0,
7661             paintbucket_offset_changed, 1, 2);
7662         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7664         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7665     }
7667     /* Auto Gap */
7668     {
7669         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7671         GList* items = 0;
7672         gint count = 0;
7673         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7674         {
7675             GtkTreeIter iter;
7676             gtk_list_store_append( model, &iter );
7677             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7678             count++;
7679         }
7680         g_list_free( items );
7681         items = 0;
7682         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7683         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7684         ege_select_one_action_set_appearance( act2, "compact" );
7685         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7686         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7687         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7688         g_object_set_data( holder, "autogap_action", act2 );
7689     }
7691     /* Reset */
7692     {
7693         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7694                                           _("Defaults"),
7695                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7696                                           GTK_STOCK_CLEAR );
7697         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7698         gtk_action_group_add_action( mainActions, act );
7699         gtk_action_set_sensitive( act, TRUE );
7700     }
7704 /*
7705   Local Variables:
7706   mode:c++
7707   c-file-style:"stroustrup"
7708   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7709   indent-tabs-mode:nil
7710   fill-column:99
7711   End:
7712 */
7713 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :