Code

Warning cleanup
[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         "    <toolitem action='SprayWidthAction' />"
311         "    <separator />"
312         "    <toolitem action='SprayPressureAction' />"
313         "    <separator />"
314         "    <toolitem action='SprayPopulationAction' />"
315         "    <separator />"
316         "    <toolitem action='SprayMeanAction' />"
317         "    <toolitem action='SprayStandard_deviationAction' />"
318         "    <separator />"
319         "    <toolitem action='DialogSprayOption' />"
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_SPRAY_OPTION,
747         SP_VERB_DIALOG_DISPLAY,
748         SP_VERB_DIALOG_FILL_STROKE,
749         SP_VERB_DIALOG_NAMEDVIEW,
750         SP_VERB_DIALOG_TEXT,
751         SP_VERB_DIALOG_XML_EDITOR,
752         SP_VERB_DIALOG_LAYERS,
753         SP_VERB_EDIT_CLONE,
754         SP_VERB_EDIT_COPY,
755         SP_VERB_EDIT_CUT,
756         SP_VERB_EDIT_DUPLICATE,
757         SP_VERB_EDIT_PASTE,
758         SP_VERB_EDIT_REDO,
759         SP_VERB_EDIT_UNDO,
760         SP_VERB_EDIT_UNLINK_CLONE,
761         SP_VERB_FILE_EXPORT,
762         SP_VERB_FILE_IMPORT,
763         SP_VERB_FILE_NEW,
764         SP_VERB_FILE_OPEN,
765         SP_VERB_FILE_PRINT,
766         SP_VERB_FILE_SAVE,
767         SP_VERB_OBJECT_TO_CURVE,
768         SP_VERB_SELECTION_GROUP,
769         SP_VERB_SELECTION_OUTLINE,
770         SP_VERB_SELECTION_UNGROUP,
771         SP_VERB_ZOOM_1_1,
772         SP_VERB_ZOOM_1_2,
773         SP_VERB_ZOOM_2_1,
774         SP_VERB_ZOOM_DRAWING,
775         SP_VERB_ZOOM_IN,
776         SP_VERB_ZOOM_NEXT,
777         SP_VERB_ZOOM_OUT,
778         SP_VERB_ZOOM_PAGE,
779         SP_VERB_ZOOM_PAGE_WIDTH,
780         SP_VERB_ZOOM_PREV,
781         SP_VERB_ZOOM_SELECTION,
782     };
784     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
786     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
787     Glib::RefPtr<Gtk::ActionGroup> mainActions;
788     if ( groups.find(desktop) != groups.end() ) {
789         mainActions = groups[desktop];
790     }
792     if ( !mainActions ) {
793         mainActions = Gtk::ActionGroup::create("main");
794         groups[desktop] = mainActions;
795     }
797     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
798         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
799         if ( verb ) {
800             if (!mainActions->get_action(verb->get_id())) {
801                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
802                 mainActions->add(Glib::wrap(act));
803             }
804         }
805     }
807     if ( !mainActions->get_action("ToolZoom") ) {
808         GtkTooltips *tt = gtk_tooltips_new();
809         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
810             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
811             if ( va ) {
812                 mainActions->add(va);
813                 if ( i == 0 ) {
814                     va->set_active(true);
815                 }
816             }
817         }
818     }
821     return mainActions;
825 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
827     gtk_widget_set_size_request( widget,
828                                  widget->allocation.width,
829                                  widget->allocation.height );
832 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
834     gtk_widget_set_size_request( widget, -1, -1 );
839 GtkWidget *
840 sp_tool_toolbox_new()
842     GtkTooltips *tt = gtk_tooltips_new();
843     GtkWidget* tb = gtk_toolbar_new();
844     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
845     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
847     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
848     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
850     gtk_widget_set_sensitive(tb, FALSE);
852     GtkWidget *hb = gtk_handle_box_new();
853     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
854     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
855     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
857     gtk_container_add(GTK_CONTAINER(hb), tb);
858     gtk_widget_show(GTK_WIDGET(tb));
860     sigc::connection* conn = new sigc::connection;
861     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
863     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
864     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
866     return hb;
869 GtkWidget *
870 sp_aux_toolbox_new()
872     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
874     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
876     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
878     gtk_widget_set_sensitive(tb, FALSE);
880     GtkWidget *hb = gtk_handle_box_new();
881     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
882     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
883     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
885     gtk_container_add(GTK_CONTAINER(hb), tb);
886     gtk_widget_show(GTK_WIDGET(tb));
888     sigc::connection* conn = new sigc::connection;
889     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
891     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
892     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
894     return hb;
897 //####################################
898 //# Commands Bar
899 //####################################
901 GtkWidget *
902 sp_commands_toolbox_new()
904     GtkWidget *tb = gtk_toolbar_new();
906     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
907     gtk_widget_set_sensitive(tb, FALSE);
909     GtkWidget *hb = gtk_handle_box_new();
910     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
911     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
912     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
914     gtk_container_add(GTK_CONTAINER(hb), tb);
915     gtk_widget_show(GTK_WIDGET(tb));
917     sigc::connection* conn = new sigc::connection;
918     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
920     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
921     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
923     return hb;
926 GtkWidget *
927 sp_snap_toolbox_new()
929         GtkWidget *tb = gtk_vbox_new(FALSE, 0);
930         gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
931         g_object_set_data(G_OBJECT(tb), "desktop", NULL);
933         //GtkWidget *tb = gtk_toolbar_new();
934     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
936     gtk_widget_set_sensitive(tb, FALSE);
938     GtkWidget *hb = gtk_handle_box_new();
939     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
940     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
941     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
943     gtk_container_add(GTK_CONTAINER(hb), tb);
944     gtk_widget_show(GTK_WIDGET(tb));
946     sigc::connection* conn = new sigc::connection;
947     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
949     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
950     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
952     return hb;
955 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
956                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
957                                                        Glib::ustring const &path, gdouble def,
958                                                        GtkWidget *focusTarget,
959                                                        GtkWidget *us,
960                                                        GObject *dataKludge,
961                                                        gboolean altx, gchar const *altx_mark,
962                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
963                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
964                                                        void (*callback)(GtkAdjustment *, GObject *),
965                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
967     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
968     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
969                                                              lower, upper, step, page, 0 ) );
970     if (us) {
971         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
972     }
974     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
976     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
977     if ( shortLabel ) {
978         g_object_set( act, "short_label", shortLabel, NULL );
979     }
981     if ( (descrCount > 0) && descrLabels && descrValues ) {
982         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
983     }
985     if ( focusTarget ) {
986         ege_adjustment_action_set_focuswidget( act, focusTarget );
987     }
989     if ( altx && altx_mark ) {
990         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
991     }
993     if ( dataKludge ) {
994         // Rather lame, but it's the only place where we need to get the entry name
995         // but we don't have an Entry
996         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
997     }
999     // Using a cast just to make sure we pass in the right kind of function pointer
1000     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1002     return act;
1006 //####################################
1007 //# node editing callbacks
1008 //####################################
1010 /**
1011  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1012  */
1013 static ShapeEditor *get_current_shape_editor()
1015     if (!SP_ACTIVE_DESKTOP) {
1016         return NULL;
1017     }
1019     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1021     if (!SP_IS_NODE_CONTEXT(event_context)) {
1022         return NULL;
1023     }
1025     return event_context->shape_editor;
1029 void
1030 sp_node_path_edit_add(void)
1032     ShapeEditor *shape_editor = get_current_shape_editor();
1033     if (shape_editor) shape_editor->add_node();
1036 void
1037 sp_node_path_edit_delete(void)
1039     ShapeEditor *shape_editor = get_current_shape_editor();
1040     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1043 void
1044 sp_node_path_edit_delete_segment(void)
1046     ShapeEditor *shape_editor = get_current_shape_editor();
1047     if (shape_editor) shape_editor->delete_segment();
1050 void
1051 sp_node_path_edit_break(void)
1053     ShapeEditor *shape_editor = get_current_shape_editor();
1054     if (shape_editor) shape_editor->break_at_nodes();
1057 void
1058 sp_node_path_edit_join(void)
1060     ShapeEditor *shape_editor = get_current_shape_editor();
1061     if (shape_editor) shape_editor->join_nodes();
1064 void
1065 sp_node_path_edit_join_segment(void)
1067     ShapeEditor *shape_editor = get_current_shape_editor();
1068     if (shape_editor) shape_editor->join_segments();
1071 void
1072 sp_node_path_edit_toline(void)
1074     ShapeEditor *shape_editor = get_current_shape_editor();
1075     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1078 void
1079 sp_node_path_edit_tocurve(void)
1081     ShapeEditor *shape_editor = get_current_shape_editor();
1082     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1085 void
1086 sp_node_path_edit_cusp(void)
1088     ShapeEditor *shape_editor = get_current_shape_editor();
1089     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1092 void
1093 sp_node_path_edit_smooth(void)
1095     ShapeEditor *shape_editor = get_current_shape_editor();
1096     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1099 void
1100 sp_node_path_edit_symmetrical(void)
1102     ShapeEditor *shape_editor = get_current_shape_editor();
1103     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1106 void
1107 sp_node_path_edit_auto(void)
1109     ShapeEditor *shape_editor = get_current_shape_editor();
1110     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1113 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1114     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1115     bool show = gtk_toggle_action_get_active( act );
1116     prefs->setBool("/tools/nodes/show_handles",  show);
1117     ShapeEditor *shape_editor = get_current_shape_editor();
1118     if (shape_editor) shape_editor->show_handles(show);
1121 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1122     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1123     bool show = gtk_toggle_action_get_active( act );
1124     prefs->setBool("/tools/nodes/show_helperpath",  show);
1125     ShapeEditor *shape_editor = get_current_shape_editor();
1126     if (shape_editor) shape_editor->show_helperpath(show);
1129 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1130     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1133 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1134     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1137 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1138     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1141 /* is called when the node selection is modified */
1142 static void
1143 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1145     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1146     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1147     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1148     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1150     // quit if run by the attr_changed listener
1151     if (g_object_get_data( tbl, "freeze" )) {
1152         return;
1153     }
1155     // in turn, prevent listener from responding
1156     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1158     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1159     SPUnit const *unit = tracker->getActiveUnit();
1161     ShapeEditor *shape_editor = get_current_shape_editor();
1162     if (shape_editor && shape_editor->has_nodepath()) {
1163         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1164         int n_selected = 0;
1165         if (nodepath) {
1166             n_selected = nodepath->numSelected();
1167         }
1169         if (n_selected == 0) {
1170             gtk_action_set_sensitive(xact, FALSE);
1171             gtk_action_set_sensitive(yact, FALSE);
1172         } else {
1173             gtk_action_set_sensitive(xact, TRUE);
1174             gtk_action_set_sensitive(yact, TRUE);
1175             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1176             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1178             if (n_selected == 1) {
1179                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1180                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1181                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1182                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1183                 }
1184             } else {
1185                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1186                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1187                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1188                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1189                        selected nodes don't coincide (in this case we use the coordinates of the center
1190                        of the bounding box). So the entries are never set to zero. */
1191                     // FIXME: Maybe we should clear the entry if several nodes are selected
1192                     //        instead of providing a kind of average value
1193                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1194                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1195                 }
1196             }
1197         }
1198     } else {
1199         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1200         gtk_action_set_sensitive(xact, FALSE);
1201         gtk_action_set_sensitive(yact, FALSE);
1202     }
1204     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1207 static void
1208 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1210     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1211     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1213     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1214     SPUnit const *unit = tracker->getActiveUnit();
1216     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1217         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1218     }
1220     // quit if run by the attr_changed listener
1221     if (g_object_get_data( tbl, "freeze" )) {
1222         return;
1223     }
1225     // in turn, prevent listener from responding
1226     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1228     ShapeEditor *shape_editor = get_current_shape_editor();
1229     if (shape_editor && shape_editor->has_nodepath()) {
1230         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1231         if (!strcmp(value_name, "x")) {
1232             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1233         }
1234         if (!strcmp(value_name, "y")) {
1235             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1236         }
1237     }
1239     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1242 static void
1243 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1245     sp_node_path_value_changed(adj, tbl, "x");
1248 static void
1249 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1251     sp_node_path_value_changed(adj, tbl, "y");
1254 void
1255 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1257     {
1258     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1259     SPItem *item = selection->singleItem();
1260     if (item && SP_IS_LPE_ITEM(item)) {
1261        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1262            gtk_action_set_sensitive(w, TRUE);
1263        } else {
1264            gtk_action_set_sensitive(w, FALSE);
1265        }
1266     } else {
1267        gtk_action_set_sensitive(w, FALSE);
1268     }
1269     }
1271     {
1272     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1273     SPItem *item = selection->singleItem();
1274     if (item && item->clip_ref && item->clip_ref->getObject()) {
1275        gtk_action_set_sensitive(w, TRUE);
1276     } else {
1277        gtk_action_set_sensitive(w, FALSE);
1278     }
1279     }
1281     {
1282     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1283     SPItem *item = selection->singleItem();
1284     if (item && item->mask_ref && item->mask_ref->getObject()) {
1285        gtk_action_set_sensitive(w, TRUE);
1286     } else {
1287        gtk_action_set_sensitive(w, FALSE);
1288     }
1289     }
1292 void
1293 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1295     sp_node_toolbox_sel_changed (selection, tbl);
1300 //################################
1301 //##    Node Editing Toolbox    ##
1302 //################################
1304 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1306     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1307     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1308     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1309     g_object_set_data( holder, "tracker", tracker );
1311     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1313     {
1314         InkAction* inky = ink_action_new( "NodeInsertAction",
1315                                           _("Insert node"),
1316                                           _("Insert new nodes into selected segments"),
1317                                           INKSCAPE_ICON_NODE_ADD,
1318                                           secondarySize );
1319         g_object_set( inky, "short_label", _("Insert"), NULL );
1320         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1321         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1322     }
1324     {
1325         InkAction* inky = ink_action_new( "NodeDeleteAction",
1326                                           _("Delete node"),
1327                                           _("Delete selected nodes"),
1328                                           INKSCAPE_ICON_NODE_DELETE,
1329                                           secondarySize );
1330         g_object_set( inky, "short_label", _("Delete"), NULL );
1331         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1332         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1333     }
1335     {
1336         InkAction* inky = ink_action_new( "NodeJoinAction",
1337                                           _("Join endnodes"),
1338                                           _("Join selected endnodes"),
1339                                           INKSCAPE_ICON_NODE_JOIN,
1340                                           secondarySize );
1341         g_object_set( inky, "short_label", _("Join"), NULL );
1342         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1343         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1344     }
1346     {
1347         InkAction* inky = ink_action_new( "NodeBreakAction",
1348                                           _("Break nodes"),
1349                                           _("Break path at selected nodes"),
1350                                           INKSCAPE_ICON_NODE_BREAK,
1351                                           secondarySize );
1352         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1353         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1354     }
1357     {
1358         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1359                                           _("Join with segment"),
1360                                           _("Join selected endnodes with a new segment"),
1361                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1362                                           secondarySize );
1363         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1364         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365     }
1367     {
1368         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1369                                           _("Delete segment"),
1370                                           _("Delete segment between two non-endpoint nodes"),
1371                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1372                                           secondarySize );
1373         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1374         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375     }
1377     {
1378         InkAction* inky = ink_action_new( "NodeCuspAction",
1379                                           _("Node Cusp"),
1380                                           _("Make selected nodes corner"),
1381                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1382                                           secondarySize );
1383         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1384         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1385     }
1387     {
1388         InkAction* inky = ink_action_new( "NodeSmoothAction",
1389                                           _("Node Smooth"),
1390                                           _("Make selected nodes smooth"),
1391                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1392                                           secondarySize );
1393         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1394         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1395     }
1397     {
1398         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1399                                           _("Node Symmetric"),
1400                                           _("Make selected nodes symmetric"),
1401                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1402                                           secondarySize );
1403         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1404         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1405     }
1407     {
1408         InkAction* inky = ink_action_new( "NodeAutoAction",
1409                                           _("Node Auto"),
1410                                           _("Make selected nodes auto-smooth"),
1411                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1412                                           secondarySize );
1413         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1414         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1415     }
1417     {
1418         InkAction* inky = ink_action_new( "NodeLineAction",
1419                                           _("Node Line"),
1420                                           _("Make selected segments lines"),
1421                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1422                                           secondarySize );
1423         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1424         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1425     }
1427     {
1428         InkAction* inky = ink_action_new( "NodeCurveAction",
1429                                           _("Node Curve"),
1430                                           _("Make selected segments curves"),
1431                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1432                                           secondarySize );
1433         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1434         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1435     }
1437     {
1438         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1439                                                       _("Show Handles"),
1440                                                       _("Show the Bezier handles of selected nodes"),
1441                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1442                                                       Inkscape::ICON_SIZE_DECORATION );
1443         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1444         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1445         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1446     }
1448     {
1449         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1450                                                       _("Show Outline"),
1451                                                       _("Show the outline of the path"),
1452                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1453                                                       Inkscape::ICON_SIZE_DECORATION );
1454         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1455         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1456         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1457     }
1459     {
1460         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1461                                           _("Next path effect parameter"),
1462                                           _("Show next path effect parameter for editing"),
1463                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1464                                           Inkscape::ICON_SIZE_DECORATION );
1465         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1466         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1467         g_object_set_data( holder, "nodes_lpeedit", inky);
1468     }
1470     {
1471         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1472                                           _("Edit clipping path"),
1473                                           _("Edit the clipping path of the object"),
1474                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1475                                           Inkscape::ICON_SIZE_DECORATION );
1476         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1477         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1478         g_object_set_data( holder, "nodes_clippathedit", inky);
1479     }
1481     {
1482         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1483                                           _("Edit mask path"),
1484                                           _("Edit the mask of the object"),
1485                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1486                                           Inkscape::ICON_SIZE_DECORATION );
1487         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1488         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1489         g_object_set_data( holder, "nodes_maskedit", inky);
1490     }
1492     /* X coord of selected node(s) */
1493     {
1494         EgeAdjustmentAction* eact = 0;
1495         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1496         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1497         eact = create_adjustment_action( "NodeXAction",
1498                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1499                                          "/tools/nodes/Xcoord", 0,
1500                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1501                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1502                                          labels, values, G_N_ELEMENTS(labels),
1503                                          sp_node_path_x_value_changed );
1504         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1505         g_object_set_data( holder, "nodes_x_action", eact );
1506         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1507         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1508     }
1510     /* Y coord of selected node(s) */
1511     {
1512         EgeAdjustmentAction* eact = 0;
1513         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1514         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1515         eact = create_adjustment_action( "NodeYAction",
1516                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1517                                          "/tools/nodes/Ycoord", 0,
1518                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1519                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1520                                          labels, values, G_N_ELEMENTS(labels),
1521                                          sp_node_path_y_value_changed );
1522         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1523         g_object_set_data( holder, "nodes_y_action", eact );
1524         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1525         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1526     }
1528     // add the units menu
1529     {
1530         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1531         gtk_action_group_add_action( mainActions, act );
1532     }
1535     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1537     //watch selection
1538     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1540     sigc::connection *c_selection_changed =
1541         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1542                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1543     pool->add_connection ("selection-changed", c_selection_changed);
1545     sigc::connection *c_selection_modified =
1546         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1547                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1548     pool->add_connection ("selection-modified", c_selection_modified);
1550     sigc::connection *c_subselection_changed =
1551         new sigc::connection (desktop->connectToolSubselectionChanged
1552                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1553     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1555     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1557     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1558 } // end of sp_node_toolbox_prep()
1561 //########################
1562 //##    Zoom Toolbox    ##
1563 //########################
1565 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1567     // no custom GtkAction setup needed
1568 } // end of sp_zoom_toolbox_prep()
1570 void
1571 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1573     toolbox_set_desktop(toolbox,
1574                         desktop,
1575                         setup_tool_toolbox,
1576                         update_tool_toolbox,
1577                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1578                                                                          "event_context_connection")));
1582 void
1583 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1585     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1586                         desktop,
1587                         setup_aux_toolbox,
1588                         update_aux_toolbox,
1589                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1590                                                                          "event_context_connection")));
1593 void
1594 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1596     toolbox_set_desktop(toolbox,
1597                         desktop,
1598                         setup_commands_toolbox,
1599                         update_commands_toolbox,
1600                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1601                                                                          "event_context_connection")));
1604 void
1605 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1607     toolbox_set_desktop(toolbox,
1608                         desktop,
1609                         setup_snap_toolbox,
1610                         update_snap_toolbox,
1611                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1612                                                                          "event_context_connection")));
1616 static void
1617 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1619     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1620     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1622     if (old_desktop) {
1623         GList *children, *iter;
1625         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1626         for ( iter = children ; iter ; iter = iter->next ) {
1627             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1628         }
1629         g_list_free(children);
1630     }
1632     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1634     if (desktop) {
1635         gtk_widget_set_sensitive(toolbox, TRUE);
1636         setup_func(toolbox, desktop);
1637         update_func(desktop, desktop->event_context, toolbox);
1638         *conn = desktop->connectEventContextChanged
1639             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1640     } else {
1641         gtk_widget_set_sensitive(toolbox, FALSE);
1642     }
1644 } // end of toolbox_set_desktop()
1647 static void
1648 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1650     gchar const * descr =
1651         "<ui>"
1652         "  <toolbar name='ToolToolbar'>"
1653         "    <toolitem action='ToolSelector' />"
1654         "    <toolitem action='ToolNode' />"
1655         "    <toolitem action='ToolTweak' />"
1656         "    <toolitem action='ToolSpray' />"
1657         "    <toolitem action='ToolZoom' />"
1658         "    <toolitem action='ToolRect' />"
1659         "    <toolitem action='Tool3DBox' />"
1660         "    <toolitem action='ToolArc' />"
1661         "    <toolitem action='ToolStar' />"
1662         "    <toolitem action='ToolSpiral' />"
1663         "    <toolitem action='ToolPencil' />"
1664         "    <toolitem action='ToolPen' />"
1665         "    <toolitem action='ToolCalligraphic' />"
1666         "    <toolitem action='ToolEraser' />"
1667 //        "    <toolitem action='ToolLPETool' />"
1668         "    <toolitem action='ToolPaintBucket' />"
1669         "    <toolitem action='ToolText' />"
1670         "    <toolitem action='ToolConnector' />"
1671         "    <toolitem action='ToolGradient' />"
1672         "    <toolitem action='ToolDropper' />"
1673         "  </toolbar>"
1674         "</ui>";
1675     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1676     GtkUIManager* mgr = gtk_ui_manager_new();
1677     GError* errVal = 0;
1678     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1680     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1681     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1683     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1684     if ( prefs->getBool("/toolbox/icononly", true) ) {
1685         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1686     }
1687     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1688     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1690     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1691     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1693     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1695     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1696     if ( child ) {
1697         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1698     }
1700     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1701 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1705 static void
1706 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1708     gchar const *const tname = ( eventcontext
1709                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1710                                  : NULL );
1711     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1713     for (int i = 0 ; tools[i].type_name ; i++ ) {
1714         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1715         if ( act ) {
1716             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1717             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1718             if ( verbAct ) {
1719                 verbAct->set_active(setActive);
1720             }
1721         }
1722     }
1725 static void
1726 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1728     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1729     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1730     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1731     GtkUIManager* mgr = gtk_ui_manager_new();
1732     GError* errVal = 0;
1733     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1734     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1736     std::map<std::string, GtkWidget*> dataHolders;
1738     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1739         if ( aux_toolboxes[i].prep_func ) {
1740             // converted to GtkActions and UIManager
1742             GtkWidget* kludge = gtk_toolbar_new();
1743             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1744             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1745             dataHolders[aux_toolboxes[i].type_name] = kludge;
1746             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1747         } else {
1749             GtkWidget *sub_toolbox = 0;
1750             if (aux_toolboxes[i].create_func == NULL)
1751                 sub_toolbox = sp_empty_toolbox_new(desktop);
1752             else {
1753                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1754             }
1756             gtk_size_group_add_widget( grouper, sub_toolbox );
1758             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1759             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1761         }
1762     }
1764     // Second pass to create toolbars *after* all GtkActions are created
1765     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1766         if ( aux_toolboxes[i].prep_func ) {
1767             // converted to GtkActions and UIManager
1769             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1771             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1772             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1774             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1775             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1776             g_free( tmp );
1777             tmp = 0;
1779             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1780                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1781             }
1783             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1784             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1786             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1788             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1789                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1790                 swatch->setDesktop( desktop );
1791                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1792                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1793                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1794                 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 );
1795             }
1797             gtk_widget_show_all( holder );
1798             sp_set_font_size_smaller( holder );
1800             gtk_size_group_add_widget( grouper, holder );
1802             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1803             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1804         }
1805     }
1807     g_object_unref( G_OBJECT(grouper) );
1810 static void
1811 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1813     gchar const *tname = ( eventcontext
1814                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1815                            : NULL );
1816     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1817         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1818         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1819             gtk_widget_show_all(sub_toolbox);
1820             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1821         } else {
1822             gtk_widget_hide(sub_toolbox);
1823         }
1824     }
1827 static void
1828 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1830     gchar const * descr =
1831         "<ui>"
1832         "  <toolbar name='CommandsToolbar'>"
1833         "    <toolitem action='FileNew' />"
1834         "    <toolitem action='FileOpen' />"
1835         "    <toolitem action='FileSave' />"
1836         "    <toolitem action='FilePrint' />"
1837         "    <separator />"
1838         "    <toolitem action='FileImport' />"
1839         "    <toolitem action='FileExport' />"
1840         "    <separator />"
1841         "    <toolitem action='EditUndo' />"
1842         "    <toolitem action='EditRedo' />"
1843         "    <separator />"
1844         "    <toolitem action='EditCopy' />"
1845         "    <toolitem action='EditCut' />"
1846         "    <toolitem action='EditPaste' />"
1847         "    <separator />"
1848         "    <toolitem action='ZoomSelection' />"
1849         "    <toolitem action='ZoomDrawing' />"
1850         "    <toolitem action='ZoomPage' />"
1851         "    <separator />"
1852         "    <toolitem action='EditDuplicate' />"
1853         "    <toolitem action='EditClone' />"
1854         "    <toolitem action='EditUnlinkClone' />"
1855         "    <separator />"
1856         "    <toolitem action='SelectionGroup' />"
1857         "    <toolitem action='SelectionUnGroup' />"
1858         "    <separator />"
1859         "    <toolitem action='DialogFillStroke' />"
1860         "    <toolitem action='DialogText' />"
1861         "    <toolitem action='DialogLayers' />"
1862         "    <toolitem action='DialogXMLEditor' />"
1863         "    <toolitem action='DialogAlignDistribute' />"
1864         "    <separator />"
1865         "    <toolitem action='DialogPreferences' />"
1866         "    <toolitem action='DialogDocumentProperties' />"
1867         "  </toolbar>"
1868         "</ui>";
1869     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1870     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1872     GtkUIManager* mgr = gtk_ui_manager_new();
1873     GError* errVal = 0;
1875     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1876     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1878     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1879     if ( prefs->getBool("/toolbox/icononly", true) ) {
1880         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1881     }
1883     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1884     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1886     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1887     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1890     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1892     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1893     if ( child ) {
1894         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1895     }
1897     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1900 static void
1901 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1905 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1907         if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1908                 return;
1909         }
1911         gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1912         g_assert(ptr != NULL);
1914         SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1915         SPNamedView *nv = sp_desktop_namedview(dt);
1916         SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1918         if (dt == NULL || nv == NULL) {
1919                 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1920                 return;
1921         }
1923         Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1925         if (repr == NULL) {
1926                 g_warning("This namedview doesn't have a xml representation attached!");
1927                 return;
1928         }
1930         bool saved = sp_document_get_undo_sensitive(doc);
1931         sp_document_set_undo_sensitive(doc, false);
1933         bool v = false;
1934         SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1936         switch (attr) {
1937         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1938                 dt->toggleSnapGlobal();
1939                 break;
1940         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1941                 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1942                 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1943                 break;
1944         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1945                 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1946                 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1947                 break;
1948         case SP_ATTR_INKSCAPE_BBOX_NODES:
1949                 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1950                 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1951                 break;
1952         case SP_ATTR_INKSCAPE_SNAP_NODES:
1953                 v = nv->snap_manager.snapprefs.getSnapModeNode();
1954                 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1955                 break;
1956         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1957                 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1958                 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1959                 break;
1960         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1961                 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1962                 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1963                 break;
1964         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1965                 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1966                 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1967                 break;
1968         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1969                 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1970                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1971                 break;
1972         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1973                 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1974                 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1975                 break;
1976         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1977                 v = nv->snap_manager.snapprefs.getSnapToGrids();
1978                 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1979                 break;
1980         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1981                 v = nv->snap_manager.snapprefs.getSnapToGuides();
1982                 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1983                 break;
1984         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1985                 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1986                 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1987                 break;
1988         /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1989                 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1990                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1991                 break;*/
1992         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1993                 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1994                 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1995                 break;
1996         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1997                 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1998                 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1999                 break;
2000         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2001                 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2002                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2003                 break;
2004         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2005                 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2006                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2007                 break;
2008         default:
2009                 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2010                 break;
2011         }
2013         // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2014         doc->setModifiedSinceSave();
2016         sp_document_set_undo_sensitive(doc, saved);
2019 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2021         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2022         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2024         gchar const * descr =
2025                 "<ui>"
2026                 "  <toolbar name='SnapToolbar'>"
2027                 "    <toolitem action='ToggleSnapGlobal' />"
2028                         "    <separator />"
2029                         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2030                         "    <toolitem action='ToggleSnapToBBoxPath' />"
2031                         "    <toolitem action='ToggleSnapToBBoxNode' />"
2032                         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2033                         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2034                         "    <separator />"
2035                         "    <toolitem action='ToggleSnapFromNode' />"
2036                         "    <toolitem action='ToggleSnapToItemPath' />"
2037                         "    <toolitem action='ToggleSnapToPathIntersections' />"
2038                         "    <toolitem action='ToggleSnapToItemNode' />"
2039                         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2040                         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2041                         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2042                         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2043                         "    <separator />"
2044                         "    <toolitem action='ToggleSnapToPageBorder' />"
2045                         "    <toolitem action='ToggleSnapToGrids' />"
2046                         "    <toolitem action='ToggleSnapToGuides' />"
2047                   //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2048                         "  </toolbar>"
2049                 "</ui>";
2051         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2053         {
2054                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2055                     _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2056                     SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2058                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2059                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2060         }
2062         {
2063                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2064                     _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2065                     secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2067                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2068                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2069         }
2071         {
2072                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2073                     _("Bounding box edges"), _("Snap to edges of a bounding box"),
2074                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2076                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2077                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2078         }
2080         {
2081                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2082                     _("Bounding box corners"), _("Snap to bounding box corners"),
2083                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2085                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2086                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2087         }
2089         {
2090                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2091                     _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2092                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2093                     SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_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("ToggleSnapToFromBBoxCenters",
2101                     _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2102                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
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("ToggleSnapFromNode",
2110                     _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2112                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2113                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2114         }
2116         {
2117                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2118                     _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2119                     SP_ATTR_INKSCAPE_OBJECT_PATHS);
2121                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2122                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2123         }
2125         {
2126                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2127                     _("Path intersections"), _("Snap to path intersections"),
2128                     INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2130                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2131                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2132         }
2134         {
2135                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2136                     _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2137                     SP_ATTR_INKSCAPE_OBJECT_NODES);
2139                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2140                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2141         }
2143         {
2144                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2145                     _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2146                     secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2148                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2149                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2150         }
2152         {
2153                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2154                     _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2155                     INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2157                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2158                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2159         }
2161         {
2162                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2163                     _("Object Centers"), _("Snap from and to centers of objects"),
2164                     INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2166                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2167                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2168         }
2170         {
2171                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2172                     _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2173                     INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2175                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2176                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2177         }
2179         {
2180                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2181                     _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2182                     secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2184                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2185                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2186         }
2188         {
2189                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2190                     _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2191                     SP_ATTR_INKSCAPE_SNAP_GRIDS);
2193                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2194                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2195         }
2197         {
2198                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2199                     _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2200                     SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2202                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2203                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2204         }
2206         /*{
2207                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2208                     _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2209                     INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2210                     SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2212                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2213                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2214         }*/
2216     GtkUIManager* mgr = gtk_ui_manager_new();
2217     GError* errVal = 0;
2219     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2220     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2222     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2223     if ( prefs->getBool("/toolbox/icononly", true) ) {
2224         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2225     }
2227     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2228     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2230     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2231     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2233     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2235     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2236     if ( child ) {
2237         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2238     }
2240     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2244 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2246         g_assert(desktop != NULL);
2247         g_assert(toolbox != NULL);
2249         SPNamedView *nv = sp_desktop_namedview(desktop);
2250         if (nv == NULL) {
2251                 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2252                 return;
2253         }
2255         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2257         Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2258         Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2259         Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2260         Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2261         Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2262         Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2263         Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2264         Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2265         Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2266         Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2267         Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2268         Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2269         Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2270         Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2271         Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2272         //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2273         Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2274         Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2277         if (!act1) {
2278                 return; // The snap actions haven't been defined yet (might be the case during startup)
2279         }
2281         // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2282         // changes in our document because we're only updating the UI;
2283         // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2284         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2286         bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2287         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2289         bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2290         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2291         gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2293         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2294         gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2295         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2296         gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2297         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2298         gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2299         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2300         gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2302         bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2303         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2304         gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2306         bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2307         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2308         gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2309         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2310         gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2311         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2312         gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2313         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2314         gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2315         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2316         gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2317         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2318         gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2319         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2320         gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2322         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2323         gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2324         //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2325         //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2327         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2328         gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2329         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2330         gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2333         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2336 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2338     gtk_widget_show(toolbox_toplevel);
2339     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2341     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2342     if (!shown_toolbox) {
2343         return;
2344     }
2345     gtk_widget_show(toolbox);
2347     gtk_widget_show_all(shown_toolbox);
2350 static GtkWidget *
2351 sp_empty_toolbox_new(SPDesktop *desktop)
2353     GtkWidget *tbl = gtk_toolbar_new();
2354     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2355     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2357     gtk_widget_show_all(tbl);
2358     sp_set_font_size_smaller (tbl);
2360     return tbl;
2363 #define MODE_LABEL_WIDTH 70
2365 //########################
2366 //##       Star         ##
2367 //########################
2369 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2371     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2373     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2374         // do not remember prefs if this call is initiated by an undo change, because undoing object
2375         // creation sets bogus values to its attributes before it is deleted
2376         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2377         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2378     }
2380     // quit if run by the attr_changed listener
2381     if (g_object_get_data( dataKludge, "freeze" )) {
2382         return;
2383     }
2385     // in turn, prevent listener from responding
2386     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2388     bool modmade = false;
2390     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2391     GSList const *items = selection->itemList();
2392     for (; items != NULL; items = items->next) {
2393         if (SP_IS_STAR((SPItem *) items->data)) {
2394             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2395             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2396             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2397                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2398                                     + M_PI / (gint)adj->value));
2399             SP_OBJECT((SPItem *) items->data)->updateRepr();
2400             modmade = true;
2401         }
2402     }
2403     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2404                                    _("Star: Change number of corners"));
2406     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2409 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2411     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2413     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2414         if (!IS_NAN(adj->value)) {
2415                         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2416                         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2417         }
2418     }
2420     // quit if run by the attr_changed listener
2421     if (g_object_get_data( dataKludge, "freeze" )) {
2422         return;
2423     }
2425     // in turn, prevent listener from responding
2426     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2428     bool modmade = false;
2429     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2430     GSList const *items = selection->itemList();
2431     for (; items != NULL; items = items->next) {
2432         if (SP_IS_STAR((SPItem *) items->data)) {
2433             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2435             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2436             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2437             if (r2 < r1) {
2438                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2439             } else {
2440                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2441             }
2443             SP_OBJECT((SPItem *) items->data)->updateRepr();
2444             modmade = true;
2445         }
2446     }
2448     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2449                                    _("Star: Change spoke ratio"));
2451     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2454 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2456     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2457     bool flat = ege_select_one_action_get_active( act ) == 0;
2459     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2460         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2461         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2462     }
2464     // quit if run by the attr_changed listener
2465     if (g_object_get_data( dataKludge, "freeze" )) {
2466         return;
2467     }
2469     // in turn, prevent listener from responding
2470     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2472     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2473     GSList const *items = selection->itemList();
2474     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2475     bool modmade = false;
2477     if ( prop_action ) {
2478         gtk_action_set_sensitive( prop_action, !flat );
2479     }
2481     for (; items != NULL; items = items->next) {
2482         if (SP_IS_STAR((SPItem *) items->data)) {
2483             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2484             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2485             SP_OBJECT((SPItem *) items->data)->updateRepr();
2486             modmade = true;
2487         }
2488     }
2490     if (modmade) {
2491         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2492                          flat ? _("Make polygon") : _("Make star"));
2493     }
2495     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2498 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2500     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2502     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2503         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2504         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2505     }
2507     // quit if run by the attr_changed listener
2508     if (g_object_get_data( dataKludge, "freeze" )) {
2509         return;
2510     }
2512     // in turn, prevent listener from responding
2513     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2515     bool modmade = false;
2517     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2518     GSList const *items = selection->itemList();
2519     for (; items != NULL; items = items->next) {
2520         if (SP_IS_STAR((SPItem *) items->data)) {
2521             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2522             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2523             SP_OBJECT(items->data)->updateRepr();
2524             modmade = true;
2525         }
2526     }
2527     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2528                                    _("Star: Change rounding"));
2530     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2533 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2535     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2537     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2538         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2539         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2540     }
2542     // quit if run by the attr_changed listener
2543     if (g_object_get_data( dataKludge, "freeze" )) {
2544         return;
2545     }
2547     // in turn, prevent listener from responding
2548     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2550     bool modmade = false;
2552     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2553     GSList const *items = selection->itemList();
2554     for (; items != NULL; items = items->next) {
2555         if (SP_IS_STAR((SPItem *) items->data)) {
2556             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2557             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2558             SP_OBJECT(items->data)->updateRepr();
2559             modmade = true;
2560         }
2561     }
2562     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2563                                    _("Star: Change randomization"));
2565     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2569 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2570                                        gchar const */*old_value*/, gchar const */*new_value*/,
2571                                        bool /*is_interactive*/, gpointer data)
2573     GtkWidget *tbl = GTK_WIDGET(data);
2575     // quit if run by the _changed callbacks
2576     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2577         return;
2578     }
2580     // in turn, prevent callbacks from responding
2581     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2583     GtkAdjustment *adj = 0;
2585     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2586     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2588     if (!strcmp(name, "inkscape:randomized")) {
2589         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2590         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2591     } else if (!strcmp(name, "inkscape:rounded")) {
2592         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2593         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2594     } else if (!strcmp(name, "inkscape:flatsided")) {
2595         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2596         char const *flatsides = repr->attribute("inkscape:flatsided");
2597         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2598         if ( flatsides && !strcmp(flatsides,"false") ) {
2599             ege_select_one_action_set_active( flat_action, 1 );
2600             gtk_action_set_sensitive( prop_action, TRUE );
2601         } else {
2602             ege_select_one_action_set_active( flat_action, 0 );
2603             gtk_action_set_sensitive( prop_action, FALSE );
2604         }
2605     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2606         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2607         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2608         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2609         if (r2 < r1) {
2610             gtk_adjustment_set_value(adj, r2/r1);
2611         } else {
2612             gtk_adjustment_set_value(adj, r1/r2);
2613         }
2614     } else if (!strcmp(name, "sodipodi:sides")) {
2615         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2616         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2617     }
2619     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2623 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2625     NULL, /* child_added */
2626     NULL, /* child_removed */
2627     star_tb_event_attr_changed,
2628     NULL, /* content_changed */
2629     NULL  /* order_changed */
2630 };
2633 /**
2634  *  \param selection Should not be NULL.
2635  */
2636 static void
2637 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2639     int n_selected = 0;
2640     Inkscape::XML::Node *repr = NULL;
2642     purge_repr_listener( tbl, tbl );
2644     for (GSList const *items = selection->itemList();
2645          items != NULL;
2646          items = items->next)
2647     {
2648         if (SP_IS_STAR((SPItem *) items->data)) {
2649             n_selected++;
2650             repr = SP_OBJECT_REPR((SPItem *) items->data);
2651         }
2652     }
2654     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2656     if (n_selected == 0) {
2657         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2658     } else if (n_selected == 1) {
2659         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2661         if (repr) {
2662             g_object_set_data( tbl, "repr", repr );
2663             Inkscape::GC::anchor(repr);
2664             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2665             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2666         }
2667     } else {
2668         // FIXME: implement averaging of all parameters for multiple selected stars
2669         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2670         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2671     }
2675 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2677     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2678     // callbacks to lump all the changes for all selected objects in one undo step
2680     GtkAdjustment *adj = 0;
2682     // fixme: make settable in prefs!
2683     gint mag = 5;
2684     gdouble prop = 0.5;
2685     gboolean flat = FALSE;
2686     gdouble randomized = 0;
2687     gdouble rounded = 0;
2689     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2690     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2692     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2693     gtk_action_set_sensitive( sb2, !flat );
2695     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2696     gtk_adjustment_set_value(adj, mag);
2697     gtk_adjustment_value_changed(adj);
2699     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2700     gtk_adjustment_set_value(adj, prop);
2701     gtk_adjustment_value_changed(adj);
2703     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2704     gtk_adjustment_set_value(adj, rounded);
2705     gtk_adjustment_value_changed(adj);
2707     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2708     gtk_adjustment_set_value(adj, randomized);
2709     gtk_adjustment_value_changed(adj);
2713 void
2714 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2716     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2717     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2718     GtkWidget *l = gtk_label_new(NULL);
2719     gtk_label_set_markup(GTK_LABEL(l), title);
2720     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2721     if ( GTK_IS_TOOLBAR(tbl) ) {
2722         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2723     } else {
2724         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2725     }
2726     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2730 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2732     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2734     {
2735         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2736         ege_output_action_set_use_markup( act, TRUE );
2737         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2738         g_object_set_data( holder, "mode_action", act );
2739     }
2741     {
2742         EgeAdjustmentAction* eact = 0;
2743         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2744         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2746         /* Flatsided checkbox */
2747         {
2748             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2750             GtkTreeIter iter;
2751             gtk_list_store_append( model, &iter );
2752             gtk_list_store_set( model, &iter,
2753                                 0, _("Polygon"),
2754                                 1, _("Regular polygon (with one handle) instead of a star"),
2755                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2756                                 -1 );
2758             gtk_list_store_append( model, &iter );
2759             gtk_list_store_set( model, &iter,
2760                                 0, _("Star"),
2761                                 1, _("Star instead of a regular polygon (with one handle)"),
2762                                 2, INKSCAPE_ICON_DRAW_STAR,
2763                                 -1 );
2765             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2766             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2767             g_object_set_data( holder, "flat_action", act );
2769             ege_select_one_action_set_appearance( act, "full" );
2770             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2771             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2772             ege_select_one_action_set_icon_column( act, 2 );
2773             ege_select_one_action_set_icon_size( act, secondarySize );
2774             ege_select_one_action_set_tooltip_column( act, 1  );
2776             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2777             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2778         }
2780         /* Magnitude */
2781         {
2782         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2783         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2784         eact = create_adjustment_action( "MagnitudeAction",
2785                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2786                                          "/tools/shapes/star/magnitude", 3,
2787                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2788                                          3, 1024, 1, 5,
2789                                          labels, values, G_N_ELEMENTS(labels),
2790                                          sp_stb_magnitude_value_changed,
2791                                          1.0, 0 );
2792         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2793         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2794         }
2796         /* Spoke ratio */
2797         {
2798         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2799         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2800         eact = create_adjustment_action( "SpokeAction",
2801                                          _("Spoke ratio"), _("Spoke ratio:"),
2802                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2803                                          // Base radius is the same for the closest handle.
2804                                          _("Base radius to tip radius ratio"),
2805                                          "/tools/shapes/star/proportion", 0.5,
2806                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2807                                          0.01, 1.0, 0.01, 0.1,
2808                                          labels, values, G_N_ELEMENTS(labels),
2809                                          sp_stb_proportion_value_changed );
2810         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2811         g_object_set_data( holder, "prop_action", eact );
2812         }
2814         if ( !isFlatSided ) {
2815             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2816         } else {
2817             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2818         }
2820         /* Roundedness */
2821         {
2822         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2823         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2824         eact = create_adjustment_action( "RoundednessAction",
2825                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2826                                          "/tools/shapes/star/rounded", 0.0,
2827                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2828                                          -10.0, 10.0, 0.01, 0.1,
2829                                          labels, values, G_N_ELEMENTS(labels),
2830                                          sp_stb_rounded_value_changed );
2831         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2832         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2833         }
2835         /* Randomization */
2836         {
2837         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2838         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2839         eact = create_adjustment_action( "RandomizationAction",
2840                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2841                                          "/tools/shapes/star/randomized", 0.0,
2842                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2843                                          -10.0, 10.0, 0.001, 0.01,
2844                                          labels, values, G_N_ELEMENTS(labels),
2845                                          sp_stb_randomized_value_changed, 0.1, 3 );
2846         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2847         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2848         }
2849     }
2851     {
2852         /* Reset */
2853         {
2854             GtkAction* act = gtk_action_new( "StarResetAction",
2855                                              _("Defaults"),
2856                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2857                                              GTK_STOCK_CLEAR );
2858             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2859             gtk_action_group_add_action( mainActions, act );
2860             gtk_action_set_sensitive( act, TRUE );
2861         }
2862     }
2864     sigc::connection *connection = new sigc::connection(
2865         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2866         );
2867     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2868     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2872 //########################
2873 //##       Rect         ##
2874 //########################
2876 static void sp_rtb_sensitivize( GObject *tbl )
2878     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2879     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2880     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2882     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2883         gtk_action_set_sensitive( not_rounded, FALSE );
2884     } else {
2885         gtk_action_set_sensitive( not_rounded, TRUE );
2886     }
2890 static void
2891 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2892                           void (*setter)(SPRect *, gdouble))
2894     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2896     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2897     SPUnit const *unit = tracker->getActiveUnit();
2899     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2900         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2901         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2902     }
2904     // quit if run by the attr_changed listener
2905     if (g_object_get_data( tbl, "freeze" )) {
2906         return;
2907     }
2909     // in turn, prevent listener from responding
2910     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2912     bool modmade = false;
2913     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2914     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2915         if (SP_IS_RECT(items->data)) {
2916             if (adj->value != 0) {
2917                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2918             } else {
2919                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2920             }
2921             modmade = true;
2922         }
2923     }
2925     sp_rtb_sensitivize( tbl );
2927     if (modmade) {
2928         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2929                                    _("Change rectangle"));
2930     }
2932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2935 static void
2936 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2938     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2941 static void
2942 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2944     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2947 static void
2948 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2950     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2953 static void
2954 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2956     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2961 static void
2962 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2964     GtkAdjustment *adj = 0;
2966     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2967     gtk_adjustment_set_value(adj, 0.0);
2968     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2969     gtk_adjustment_value_changed(adj);
2971     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2972     gtk_adjustment_set_value(adj, 0.0);
2973     gtk_adjustment_value_changed(adj);
2975     sp_rtb_sensitivize( obj );
2978 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2979                                        gchar const */*old_value*/, gchar const */*new_value*/,
2980                                        bool /*is_interactive*/, gpointer data)
2982     GObject *tbl = G_OBJECT(data);
2984     // quit if run by the _changed callbacks
2985     if (g_object_get_data( tbl, "freeze" )) {
2986         return;
2987     }
2989     // in turn, prevent callbacks from responding
2990     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2992     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2993     SPUnit const *unit = tracker->getActiveUnit();
2995     gpointer item = g_object_get_data( tbl, "item" );
2996     if (item && SP_IS_RECT(item)) {
2997         {
2998             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2999             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3000             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3001         }
3003         {
3004             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3005             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3006             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3007         }
3009         {
3010             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3011             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3012             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3013         }
3015         {
3016             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3017             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3018             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3019         }
3020     }
3022     sp_rtb_sensitivize( tbl );
3024     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3028 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3029     NULL, /* child_added */
3030     NULL, /* child_removed */
3031     rect_tb_event_attr_changed,
3032     NULL, /* content_changed */
3033     NULL  /* order_changed */
3034 };
3036 /**
3037  *  \param selection should not be NULL.
3038  */
3039 static void
3040 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3042     int n_selected = 0;
3043     Inkscape::XML::Node *repr = NULL;
3044     SPItem *item = NULL;
3046     if ( g_object_get_data( tbl, "repr" ) ) {
3047         g_object_set_data( tbl, "item", NULL );
3048     }
3049     purge_repr_listener( tbl, tbl );
3051     for (GSList const *items = selection->itemList();
3052          items != NULL;
3053          items = items->next) {
3054         if (SP_IS_RECT((SPItem *) items->data)) {
3055             n_selected++;
3056             item = (SPItem *) items->data;
3057             repr = SP_OBJECT_REPR(item);
3058         }
3059     }
3061     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3063     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3065     if (n_selected == 0) {
3066         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3068         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3069         gtk_action_set_sensitive(w, FALSE);
3070         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3071         gtk_action_set_sensitive(h, FALSE);
3073     } else if (n_selected == 1) {
3074         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3075         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3077         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3078         gtk_action_set_sensitive(w, TRUE);
3079         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3080         gtk_action_set_sensitive(h, TRUE);
3082         if (repr) {
3083             g_object_set_data( tbl, "repr", repr );
3084             g_object_set_data( tbl, "item", item );
3085             Inkscape::GC::anchor(repr);
3086             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3087             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3088         }
3089     } else {
3090         // FIXME: implement averaging of all parameters for multiple selected
3091         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3092         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3093         sp_rtb_sensitivize( tbl );
3094     }
3098 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3100     EgeAdjustmentAction* eact = 0;
3101     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3103     {
3104         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3105         ege_output_action_set_use_markup( act, TRUE );
3106         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3107         g_object_set_data( holder, "mode_action", act );
3108     }
3110     // rx/ry units menu: create
3111     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3112     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3113     // fixme: add % meaning per cent of the width/height
3114     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3115     g_object_set_data( holder, "tracker", tracker );
3117     /* W */
3118     {
3119         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3120         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3121         eact = create_adjustment_action( "RectWidthAction",
3122                                          _("Width"), _("W:"), _("Width of rectangle"),
3123                                          "/tools/shapes/rect/width", 0,
3124                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3125                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3126                                          labels, values, G_N_ELEMENTS(labels),
3127                                          sp_rtb_width_value_changed );
3128         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3129         g_object_set_data( holder, "width_action", eact );
3130         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3131         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3132     }
3134     /* H */
3135     {
3136         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3137         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3138         eact = create_adjustment_action( "RectHeightAction",
3139                                          _("Height"), _("H:"), _("Height of rectangle"),
3140                                          "/tools/shapes/rect/height", 0,
3141                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3142                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3143                                          labels, values, G_N_ELEMENTS(labels),
3144                                          sp_rtb_height_value_changed );
3145         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3146         g_object_set_data( holder, "height_action", eact );
3147         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3148         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3149     }
3151     /* rx */
3152     {
3153         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3154         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3155         eact = create_adjustment_action( "RadiusXAction",
3156                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3157                                          "/tools/shapes/rect/rx", 0,
3158                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3159                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3160                                          labels, values, G_N_ELEMENTS(labels),
3161                                          sp_rtb_rx_value_changed);
3162         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3163         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3164     }
3166     /* ry */
3167     {
3168         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3169         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3170         eact = create_adjustment_action( "RadiusYAction",
3171                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3172                                          "/tools/shapes/rect/ry", 0,
3173                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3174                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3175                                          labels, values, G_N_ELEMENTS(labels),
3176                                          sp_rtb_ry_value_changed);
3177         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3178         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3179     }
3181     // add the units menu
3182     {
3183         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3184         gtk_action_group_add_action( mainActions, act );
3185     }
3187     /* Reset */
3188     {
3189         InkAction* inky = ink_action_new( "RectResetAction",
3190                                           _("Not rounded"),
3191                                           _("Make corners sharp"),
3192                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3193                                           secondarySize );
3194         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3195         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3196         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3197         g_object_set_data( holder, "not_rounded", inky );
3198     }
3200     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3201     sp_rtb_sensitivize( holder );
3203     sigc::connection *connection = new sigc::connection(
3204         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3205         );
3206     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3207     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3210 //########################
3211 //##       3D Box       ##
3212 //########################
3214 // normalize angle so that it lies in the interval [0,360]
3215 static double box3d_normalize_angle (double a) {
3216     double angle = a + ((int) (a/360.0))*360;
3217     if (angle < 0) {
3218         angle += 360.0;
3219     }
3220     return angle;
3223 static void
3224 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3225                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3226     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3227     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3228     //       are reset).
3229     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3231     if (is_infinite) {
3232         gtk_toggle_action_set_active(tact, TRUE);
3233         gtk_action_set_sensitive(act, TRUE);
3235         double angle = persp3d_get_infinite_angle(persp, axis);
3236         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3237             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3238         }
3239     } else {
3240         gtk_toggle_action_set_active(tact, FALSE);
3241         gtk_action_set_sensitive(act, FALSE);
3242     }
3245 static void
3246 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3247     if (!persp_repr) {
3248         g_print ("No perspective given to box3d_resync_toolbar().\n");
3249         return;
3250     }
3252     GtkWidget *tbl = GTK_WIDGET(data);
3253     GtkAdjustment *adj = 0;
3254     GtkAction *act = 0;
3255     GtkToggleAction *tact = 0;
3256     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3257     {
3258         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3259         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3260         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3262         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3263     }
3264     {
3265         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3266         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3267         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3269         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3270     }
3271     {
3272         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3273         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3274         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3276         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3277     }
3280 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3281                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3282                                                   bool /*is_interactive*/, gpointer data)
3284     GtkWidget *tbl = GTK_WIDGET(data);
3286     // quit if run by the attr_changed or selection changed listener
3287     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3288         return;
3289     }
3291     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3292     // sp_document_maybe_done() when the document is undo insensitive)
3293     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3295     // TODO: Only update the appropriate part of the toolbar
3296 //    if (!strcmp(name, "inkscape:vp_z")) {
3297         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3298 //    }
3300     Persp3D *persp = persp3d_get_from_repr(repr);
3301     persp3d_update_box_reprs(persp);
3303     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3306 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3308     NULL, /* child_added */
3309     NULL, /* child_removed */
3310     box3d_persp_tb_event_attr_changed,
3311     NULL, /* content_changed */
3312     NULL  /* order_changed */
3313 };
3315 /**
3316  *  \param selection Should not be NULL.
3317  */
3318 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3319 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3320 static void
3321 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3323     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3324     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3325     // update the perspectives with infinite VPs and leave the other ones untouched).
3327     Inkscape::XML::Node *persp_repr = NULL;
3328     purge_repr_listener(tbl, tbl);
3330     SPItem *item = selection->singleItem();
3331     if (item && SP_IS_BOX3D(item)) {
3332         // FIXME: Also deal with multiple selected boxes
3333         SPBox3D *box = SP_BOX3D(item);
3334         Persp3D *persp = box3d_get_perspective(box);
3335         persp_repr = SP_OBJECT_REPR(persp);
3336         if (persp_repr) {
3337             g_object_set_data(tbl, "repr", persp_repr);
3338             Inkscape::GC::anchor(persp_repr);
3339             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3340             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3341         }
3343         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3344         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3345         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3347         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3348         box3d_resync_toolbar(persp_repr, tbl);
3349         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3350     }
3353 static void
3354 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3356     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3357     SPDocument *document = sp_desktop_document(desktop);
3359     // quit if run by the attr_changed or selection changed listener
3360     if (g_object_get_data( dataKludge, "freeze" )) {
3361         return;
3362     }
3364     // in turn, prevent listener from responding
3365     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3367     //Persp3D *persp = document->current_persp3d;
3368     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3369     if (sel_persps.empty()) {
3370         // this can happen when the document is created; we silently ignore it
3371         return;
3372     }
3373     Persp3D *persp = sel_persps.front();
3375     persp->tmat.set_infinite_direction (axis, adj->value);
3376     SP_OBJECT(persp)->updateRepr();
3378     // TODO: use the correct axis here, too
3379     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3381     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3385 static void
3386 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3388     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3391 static void
3392 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3394     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3397 static void
3398 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3400     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3404 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3406     // TODO: Take all selected perspectives into account
3407     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3408     if (sel_persps.empty()) {
3409         // this can happen when the document is created; we silently ignore it
3410         return;
3411     }
3412     Persp3D *persp = sel_persps.front();
3414     bool set_infinite = gtk_toggle_action_get_active(act);
3415     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3418 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3420     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3423 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3425     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3428 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3430     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3433 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3435     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3436     EgeAdjustmentAction* eact = 0;
3437     SPDocument *document = sp_desktop_document (desktop);
3438     Persp3D *persp = document->current_persp3d;
3440     EgeAdjustmentAction* box3d_angle_x = 0;
3441     EgeAdjustmentAction* box3d_angle_y = 0;
3442     EgeAdjustmentAction* box3d_angle_z = 0;
3444     /* Angle X */
3445     {
3446         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3447         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3448         eact = create_adjustment_action( "3DBoxAngleXAction",
3449                                          _("Angle in X direction"), _("Angle X:"),
3450                                          // Translators: PL is short for 'perspective line'
3451                                          _("Angle of PLs in X direction"),
3452                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3453                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3454                                          -360.0, 360.0, 1.0, 10.0,
3455                                          labels, values, G_N_ELEMENTS(labels),
3456                                          box3d_angle_x_value_changed );
3457         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3458         g_object_set_data( holder, "box3d_angle_x_action", eact );
3459         box3d_angle_x = eact;
3460     }
3462     if (!persp3d_VP_is_finite(persp, Proj::X)) {
3463         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3464     } else {
3465         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3466     }
3469     /* VP X state */
3470     {
3471         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3472                                                       // Translators: VP is short for 'vanishing point'
3473                                                       _("State of VP in X direction"),
3474                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3475                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3476                                                       Inkscape::ICON_SIZE_DECORATION );
3477         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3478         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3479         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3480         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3481         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3482     }
3484     /* Angle Y */
3485     {
3486         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3487         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3488         eact = create_adjustment_action( "3DBoxAngleYAction",
3489                                          _("Angle in Y direction"), _("Angle Y:"),
3490                                          // Translators: PL is short for 'perspective line'
3491                                          _("Angle of PLs in Y direction"),
3492                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3493                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3494                                          -360.0, 360.0, 1.0, 10.0,
3495                                          labels, values, G_N_ELEMENTS(labels),
3496                                          box3d_angle_y_value_changed );
3497         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3498         g_object_set_data( holder, "box3d_angle_y_action", eact );
3499         box3d_angle_y = eact;
3500     }
3502     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3503         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3504     } else {
3505         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3506     }
3508     /* VP Y state */
3509     {
3510         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3511                                                       // Translators: VP is short for 'vanishing point'
3512                                                       _("State of VP in Y direction"),
3513                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3514                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3515                                                       Inkscape::ICON_SIZE_DECORATION );
3516         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3517         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3518         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3519         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3520         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3521     }
3523     /* Angle Z */
3524     {
3525         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3526         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3527         eact = create_adjustment_action( "3DBoxAngleZAction",
3528                                          _("Angle in Z direction"), _("Angle Z:"),
3529                                          // Translators: PL is short for 'perspective line'
3530                                          _("Angle of PLs in Z direction"),
3531                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3532                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3533                                          -360.0, 360.0, 1.0, 10.0,
3534                                          labels, values, G_N_ELEMENTS(labels),
3535                                          box3d_angle_z_value_changed );
3536         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3537         g_object_set_data( holder, "box3d_angle_z_action", eact );
3538         box3d_angle_z = eact;
3539     }
3541     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3542         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3543     } else {
3544         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3545     }
3547     /* VP Z state */
3548     {
3549         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3550                                                       // Translators: VP is short for 'vanishing point'
3551                                                       _("State of VP in Z direction"),
3552                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3553                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3554                                                       Inkscape::ICON_SIZE_DECORATION );
3555         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3556         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3557         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3558         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3559         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3560     }
3562     sigc::connection *connection = new sigc::connection(
3563         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3564        );
3565     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3566     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3569 //########################
3570 //##       Spiral       ##
3571 //########################
3573 static void
3574 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3576     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3578     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3579         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3580         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3581     }
3583     // quit if run by the attr_changed listener
3584     if (g_object_get_data( tbl, "freeze" )) {
3585         return;
3586     }
3588     // in turn, prevent listener from responding
3589     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3591     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3593     bool modmade = false;
3594     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3595          items != NULL;
3596          items = items->next)
3597     {
3598         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3599             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3600             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3601             SP_OBJECT((SPItem *) items->data)->updateRepr();
3602             modmade = true;
3603         }
3604     }
3606     g_free(namespaced_name);
3608     if (modmade) {
3609         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3610                                    _("Change spiral"));
3611     }
3613     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3616 static void
3617 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3619     sp_spl_tb_value_changed(adj, tbl, "revolution");
3622 static void
3623 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3625     sp_spl_tb_value_changed(adj, tbl, "expansion");
3628 static void
3629 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3631     sp_spl_tb_value_changed(adj, tbl, "t0");
3634 static void
3635 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3637     GtkWidget *tbl = GTK_WIDGET(obj);
3639     GtkAdjustment *adj;
3641     // fixme: make settable
3642     gdouble rev = 5;
3643     gdouble exp = 1.0;
3644     gdouble t0 = 0.0;
3646     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3647     gtk_adjustment_set_value(adj, rev);
3648     gtk_adjustment_value_changed(adj);
3650     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3651     gtk_adjustment_set_value(adj, exp);
3652     gtk_adjustment_value_changed(adj);
3654     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3655     gtk_adjustment_set_value(adj, t0);
3656     gtk_adjustment_value_changed(adj);
3658     spinbutton_defocus(GTK_OBJECT(tbl));
3662 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3663                                          gchar const */*old_value*/, gchar const */*new_value*/,
3664                                          bool /*is_interactive*/, gpointer data)
3666     GtkWidget *tbl = GTK_WIDGET(data);
3668     // quit if run by the _changed callbacks
3669     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3670         return;
3671     }
3673     // in turn, prevent callbacks from responding
3674     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3676     GtkAdjustment *adj;
3677     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3678     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3680     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3681     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3683     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3684     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3686     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3690 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3691     NULL, /* child_added */
3692     NULL, /* child_removed */
3693     spiral_tb_event_attr_changed,
3694     NULL, /* content_changed */
3695     NULL  /* order_changed */
3696 };
3698 static void
3699 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3701     int n_selected = 0;
3702     Inkscape::XML::Node *repr = NULL;
3704     purge_repr_listener( tbl, tbl );
3706     for (GSList const *items = selection->itemList();
3707          items != NULL;
3708          items = items->next)
3709     {
3710         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3711             n_selected++;
3712             repr = SP_OBJECT_REPR((SPItem *) items->data);
3713         }
3714     }
3716     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3718     if (n_selected == 0) {
3719         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3720     } else if (n_selected == 1) {
3721         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3723         if (repr) {
3724             g_object_set_data( tbl, "repr", repr );
3725             Inkscape::GC::anchor(repr);
3726             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3727             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3728         }
3729     } else {
3730         // FIXME: implement averaging of all parameters for multiple selected
3731         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3732         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3733     }
3737 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3739     EgeAdjustmentAction* eact = 0;
3740     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3742     {
3743         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3744         ege_output_action_set_use_markup( act, TRUE );
3745         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3746         g_object_set_data( holder, "mode_action", act );
3747     }
3749     /* Revolution */
3750     {
3751         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3752         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3753         eact = create_adjustment_action( "SpiralRevolutionAction",
3754                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3755                                          "/tools/shapes/spiral/revolution", 3.0,
3756                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3757                                          0.01, 1024.0, 0.1, 1.0,
3758                                          labels, values, G_N_ELEMENTS(labels),
3759                                          sp_spl_tb_revolution_value_changed, 1, 2);
3760         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3761     }
3763     /* Expansion */
3764     {
3765         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3766         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3767         eact = create_adjustment_action( "SpiralExpansionAction",
3768                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3769                                          "/tools/shapes/spiral/expansion", 1.0,
3770                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3771                                          0.0, 1000.0, 0.01, 1.0,
3772                                          labels, values, G_N_ELEMENTS(labels),
3773                                          sp_spl_tb_expansion_value_changed);
3774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3775     }
3777     /* T0 */
3778     {
3779         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3780         gdouble values[] = {0, 0.5, 0.9};
3781         eact = create_adjustment_action( "SpiralT0Action",
3782                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3783                                          "/tools/shapes/spiral/t0", 0.0,
3784                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3785                                          0.0, 0.999, 0.01, 1.0,
3786                                          labels, values, G_N_ELEMENTS(labels),
3787                                          sp_spl_tb_t0_value_changed);
3788         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3789     }
3791     /* Reset */
3792     {
3793         InkAction* inky = ink_action_new( "SpiralResetAction",
3794                                           _("Defaults"),
3795                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3796                                           GTK_STOCK_CLEAR,
3797                                           secondarySize );
3798         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3799         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3800     }
3803     sigc::connection *connection = new sigc::connection(
3804         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3805         );
3806     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3807     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3810 //########################
3811 //##     Pen/Pencil     ##
3812 //########################
3814 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3815 static Glib::ustring const
3816 freehand_tool_name(GObject *dataKludge)
3818     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3819     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3820              ? "/tools/freehand/pen"
3821              : "/tools/freehand/pencil" );
3824 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3826     gint mode = ege_select_one_action_get_active(act);
3828     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3829     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3831     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3833     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3834     // preparatory work here
3835     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3836         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3837         sp_pen_context_set_polyline_mode(pc);
3838     }
3841 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3843     /* Freehand mode toggle buttons */
3844     {
3845         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3846         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3847         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3849         {
3850             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3852             GtkTreeIter iter;
3853             gtk_list_store_append( model, &iter );
3854             gtk_list_store_set( model, &iter,
3855                                 0, _("Bezier"),
3856                                 1, _("Create regular Bezier path"),
3857                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3858                                 -1 );
3860             gtk_list_store_append( model, &iter );
3861             gtk_list_store_set( model, &iter,
3862                                 0, _("Spiro"),
3863                                 1, _("Create Spiro path"),
3864                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3865                                 -1 );
3867             if (!tool_is_pencil) {
3868                 gtk_list_store_append( model, &iter );
3869                 gtk_list_store_set( model, &iter,
3870                                     0, _("Zigzag"),
3871                                     1, _("Create a sequence of straight line segments"),
3872                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3873                                     -1 );
3875                 gtk_list_store_append( model, &iter );
3876                 gtk_list_store_set( model, &iter,
3877                                     0, _("Paraxial"),
3878                                     1, _("Create a sequence of paraxial line segments"),
3879                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3880                                     -1 );
3881             }
3883             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3884                                                                 "FreehandModeActionPencil" :
3885                                                                 "FreehandModeActionPen",
3886                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3887             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3889             ege_select_one_action_set_appearance( act, "full" );
3890             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3891             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3892             ege_select_one_action_set_icon_column( act, 2 );
3893             ege_select_one_action_set_icon_size( act, secondarySize );
3894             ege_select_one_action_set_tooltip_column( act, 1  );
3896             ege_select_one_action_set_active( act, freehandMode);
3897             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3898         }
3899     }
3902 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3903     gint shape = ege_select_one_action_get_active( act );
3904     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3905     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3908 /**
3909  * \brief Generate the list of freehand advanced shape option entries.
3910  */
3911 GList * freehand_shape_dropdown_items_list() {
3912     GList *glist = NULL;
3914     glist = g_list_append (glist, _("None"));
3915     glist = g_list_append (glist, _("Triangle in"));
3916     glist = g_list_append (glist, _("Triangle out"));
3917     glist = g_list_append (glist, _("Ellipse"));
3918     glist = g_list_append (glist, _("From clipboard"));
3920     return glist;
3923 static void
3924 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3925     /*advanced shape options */
3926     {
3927         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3928         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3930         GList* items = 0;
3931         gint count = 0;
3932         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3933         {
3934             GtkTreeIter iter;
3935             gtk_list_store_append( model, &iter );
3936             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3937             count++;
3938         }
3939         g_list_free( items );
3940         items = 0;
3941         EgeSelectOneAction* act1 = ege_select_one_action_new(
3942             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3943             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3944         g_object_set( act1, "short_label", _("Shape:"), NULL );
3945         ege_select_one_action_set_appearance( act1, "compact" );
3946         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3947         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3948         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3949         g_object_set_data( holder, "shape_action", act1 );
3950     }
3953 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3955     sp_add_freehand_mode_toggle(mainActions, holder, false);
3956     freehand_add_advanced_shape_options(mainActions, holder, false);
3960 static void
3961 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3963     GtkWidget *tbl = GTK_WIDGET(obj);
3965     GtkAdjustment *adj;
3967     // fixme: make settable
3968     gdouble tolerance = 4;
3970     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3971     gtk_adjustment_set_value(adj, tolerance);
3972     gtk_adjustment_value_changed(adj);
3974     spinbutton_defocus(GTK_OBJECT(tbl));
3977 static void
3978 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3980     // quit if run by the attr_changed listener
3981     if (g_object_get_data( tbl, "freeze" )) {
3982         return;
3983     }
3984     // in turn, prevent listener from responding
3985     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3986     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3987     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3988     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3991 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3992 public:
3993     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3994     {
3995         g_object_set_data(_obj, "prefobserver", this);
3996     }
3997     virtual ~PencilToleranceObserver() {
3998         if (g_object_get_data(_obj, "prefobserver") == this) {
3999                 g_object_set_data(_obj, "prefobserver", NULL);
4000         }
4001     }
4002     virtual void notify(Inkscape::Preferences::Entry const &val) {
4003         GObject* tbl = _obj;
4004         if (g_object_get_data( tbl, "freeze" )) {
4005             return;
4006         }
4007         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4009         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4011         double v = val.getDouble(adj->value);
4012         gtk_adjustment_set_value(adj, v);
4013         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4014     }
4015 private:
4016     GObject *_obj;
4017 };
4020 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4022     sp_add_freehand_mode_toggle(mainActions, holder, true);
4024     EgeAdjustmentAction* eact = 0;
4026     /* Tolerance */
4027     {
4028         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4029         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4030         eact = create_adjustment_action( "PencilToleranceAction",
4031                                          _("Smoothing:"), _("Smoothing: "),
4032                  _("How much smoothing (simplifying) is applied to the line"),
4033                                          "/tools/freehand/pencil/tolerance",
4034                                          3.0,
4035                                          GTK_WIDGET(desktop->canvas), NULL,
4036                                          holder, TRUE, "altx-pencil",
4037                                          1, 100.0, 0.5, 1.0,
4038                                          labels, values, G_N_ELEMENTS(labels),
4039                                          sp_pencil_tb_tolerance_value_changed,
4040                                          1, 2);
4041         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4042         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4044         PencilToleranceObserver *obs =
4045             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4046     }
4048     /* advanced shape options */
4049     freehand_add_advanced_shape_options(mainActions, holder, true);
4051     /* Reset */
4052     {
4053         InkAction* inky = ink_action_new( "PencilResetAction",
4054                                           _("Defaults"),
4055                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4056                                           GTK_STOCK_CLEAR,
4057                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4058         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4059         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4060     }
4062     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4067 //########################
4068 //##       Tweak        ##
4069 //########################
4071 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4073     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4077 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4079     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4080     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4083 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4085     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4086     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4089 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4091     int mode = ege_select_one_action_get_active( act );
4092     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4093     prefs->setInt("/tools/tweak/mode", mode);
4095     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4096     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4097     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4098     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4099     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4100     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4101     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4102         if (doh) gtk_action_set_sensitive (doh, TRUE);
4103         if (dos) gtk_action_set_sensitive (dos, TRUE);
4104         if (dol) gtk_action_set_sensitive (dol, TRUE);
4105         if (doo) gtk_action_set_sensitive (doo, TRUE);
4106         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4107         if (fid) gtk_action_set_sensitive (fid, FALSE);
4108     } else {
4109         if (doh) gtk_action_set_sensitive (doh, FALSE);
4110         if (dos) gtk_action_set_sensitive (dos, FALSE);
4111         if (dol) gtk_action_set_sensitive (dol, FALSE);
4112         if (doo) gtk_action_set_sensitive (doo, FALSE);
4113         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4114         if (fid) gtk_action_set_sensitive (fid, TRUE);
4115     }
4118 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4120     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4121     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4124 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4125     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4126     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4128 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4129     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4130     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4132 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4133     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4134     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4136 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4138     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4141 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4143     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4144     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4146     {
4147         /* Width */
4148         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4149         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4150         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4151                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4152                                                               "/tools/tweak/width", 15,
4153                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4154                                                               1, 100, 1.0, 10.0,
4155                                                               labels, values, G_N_ELEMENTS(labels),
4156                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4157         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4158         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4159         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4160     }
4163     {
4164         /* Force */
4165         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4166         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4167         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4168                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4169                                                               "/tools/tweak/force", 20,
4170                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4171                                                               1, 100, 1.0, 10.0,
4172                                                               labels, values, G_N_ELEMENTS(labels),
4173                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4174         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4175         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4176         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4177     }
4179     /* Mode */
4180     {
4181         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4183         GtkTreeIter iter;
4184         gtk_list_store_append( model, &iter );
4185         gtk_list_store_set( model, &iter,
4186                             0, _("Move mode"),
4187                             1, _("Move objects in any direction"),
4188                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4189                             -1 );
4191         gtk_list_store_append( model, &iter );
4192         gtk_list_store_set( model, &iter,
4193                             0, _("Move in/out mode"),
4194                             1, _("Move objects towards cursor; with Shift from cursor"),
4195                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4196                             -1 );
4198         gtk_list_store_append( model, &iter );
4199         gtk_list_store_set( model, &iter,
4200                             0, _("Move jitter mode"),
4201                             1, _("Move objects in random directions"),
4202                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4203                             -1 );
4205         gtk_list_store_append( model, &iter );
4206         gtk_list_store_set( model, &iter,
4207                             0, _("Scale mode"),
4208                             1, _("Shrink objects, with Shift enlarge"),
4209                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4210                             -1 );
4212         gtk_list_store_append( model, &iter );
4213         gtk_list_store_set( model, &iter,
4214                             0, _("Rotate mode"),
4215                             1, _("Rotate objects, with Shift counterclockwise"),
4216                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4217                             -1 );
4219         gtk_list_store_append( model, &iter );
4220         gtk_list_store_set( model, &iter,
4221                             0, _("Duplicate/delete mode"),
4222                             1, _("Duplicate objects, with Shift delete"),
4223                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4224                             -1 );
4226         gtk_list_store_append( model, &iter );
4227         gtk_list_store_set( model, &iter,
4228                             0, _("Push mode"),
4229                             1, _("Push parts of paths in any direction"),
4230                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4231                             -1 );
4233         gtk_list_store_append( model, &iter );
4234         gtk_list_store_set( model, &iter,
4235                             0, _("Shrink/grow mode"),
4236                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4237                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4238                             -1 );
4240         gtk_list_store_append( model, &iter );
4241         gtk_list_store_set( model, &iter,
4242                             0, _("Attract/repel mode"),
4243                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4244                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4245                             -1 );
4247         gtk_list_store_append( model, &iter );
4248         gtk_list_store_set( model, &iter,
4249                             0, _("Roughen mode"),
4250                             1, _("Roughen parts of paths"),
4251                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4252                             -1 );
4254         gtk_list_store_append( model, &iter );
4255         gtk_list_store_set( model, &iter,
4256                             0, _("Color paint mode"),
4257                             1, _("Paint the tool's color upon selected objects"),
4258                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4259                             -1 );
4261         gtk_list_store_append( model, &iter );
4262         gtk_list_store_set( model, &iter,
4263                             0, _("Color jitter mode"),
4264                             1, _("Jitter the colors of selected objects"),
4265                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4266                             -1 );
4268         gtk_list_store_append( model, &iter );
4269         gtk_list_store_set( model, &iter,
4270                             0, _("Blur mode"),
4271                             1, _("Blur selected objects more; with Shift, blur less"),
4272                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4273                             -1 );
4276         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4277         g_object_set( act, "short_label", _("Mode:"), NULL );
4278         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4279         g_object_set_data( holder, "mode_action", act );
4281         ege_select_one_action_set_appearance( act, "full" );
4282         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4283         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4284         ege_select_one_action_set_icon_column( act, 2 );
4285         ege_select_one_action_set_icon_size( act, secondarySize );
4286         ege_select_one_action_set_tooltip_column( act, 1  );
4288         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4289         ege_select_one_action_set_active( act, mode );
4290         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4292         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4293     }
4295     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4297     {
4298         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4299         ege_output_action_set_use_markup( act, TRUE );
4300         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4302             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4303         g_object_set_data( holder, "tweak_channels_label", act);
4304     }
4306     {
4307         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4308                                                       _("Hue"),
4309                                                       _("In color mode, act on objects' hue"),
4310                                                       NULL,
4311                                                       Inkscape::ICON_SIZE_DECORATION );
4312         //TRANSLATORS:  "H" here stands for hue
4313         g_object_set( act, "short_label", _("H"), NULL );
4314         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4315         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4316         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4317         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4318             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4319         g_object_set_data( holder, "tweak_doh", act);
4320     }
4321     {
4322         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4323                                                       _("Saturation"),
4324                                                       _("In color mode, act on objects' saturation"),
4325                                                       NULL,
4326                                                       Inkscape::ICON_SIZE_DECORATION );
4327         //TRANSLATORS: "S" here stands for Saturation
4328         g_object_set( act, "short_label", _("S"), NULL );
4329         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4330         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4331         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4332         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4333             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4334         g_object_set_data( holder, "tweak_dos", act );
4335     }
4336     {
4337         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4338                                                       _("Lightness"),
4339                                                       _("In color mode, act on objects' lightness"),
4340                                                       NULL,
4341                                                       Inkscape::ICON_SIZE_DECORATION );
4342         //TRANSLATORS: "L" here stands for Lightness
4343         g_object_set( act, "short_label", _("L"), NULL );
4344         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4345         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4346         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4347         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4348             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4349         g_object_set_data( holder, "tweak_dol", act );
4350     }
4351     {
4352         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4353                                                       _("Opacity"),
4354                                                       _("In color mode, act on objects' opacity"),
4355                                                       NULL,
4356                                                       Inkscape::ICON_SIZE_DECORATION );
4357         //TRANSLATORS: "O" here stands for Opacity
4358         g_object_set( act, "short_label", _("O"), NULL );
4359         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4360         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4361         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4362         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4363             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4364         g_object_set_data( holder, "tweak_doo", act );
4365     }
4367     {   /* Fidelity */
4368         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4369         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4370         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4371                                                               _("Fidelity"), _("Fidelity:"),
4372                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4373                                                               "/tools/tweak/fidelity", 50,
4374                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4375                                                               1, 100, 1.0, 10.0,
4376                                                               labels, values, G_N_ELEMENTS(labels),
4377                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4378         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4379         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4380         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4381             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4382         g_object_set_data( holder, "tweak_fidelity", eact );
4383     }
4386     /* Use Pressure button */
4387     {
4388         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4389                                                       _("Pressure"),
4390                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4391                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4392                                                       Inkscape::ICON_SIZE_DECORATION );
4393         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4394         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4395         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4396     }
4401 //########################
4402 //##       Spray        ##
4403 //########################
4405 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4407     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4408     prefs->setDouble( "/tools/spray/width", adj->value );
4411 /*
4412 static void sp_spray_force_value_changed( GtkAdjustment * / *adj* /, GObject * / *tbl* / )
4414     //Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4415     //prefs->setDouble( "/tools/spray/force", adj->value * 0.01 );
4417 */
4419 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4421     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4422     prefs->setDouble( "/tools/spray/mean", adj->value );
4425 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4427     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4428     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4431 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4433     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4434     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4437 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4439     int mode = ege_select_one_action_get_active( act );
4440     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4441     prefs->setInt("/tools/spray/mode", mode);
4444 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4446     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4447     prefs->setDouble( "/tools/spray/population", adj->value );
4450 /*static void spray_toggle_doh (GtkToggleAction *act, gpointer ) {
4451     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4452     prefs->setBool("/tools/spray/doh", gtk_toggle_action_get_active(act));
4454 static void spray_toggle_dos (GtkToggleAction *act, gpointer ) {
4455     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4456     prefs->setBool("/tools/spray/dos", gtk_toggle_action_get_active(act));
4458 static void spray_toggle_dol (GtkToggleAction *act, gpointer ) {
4459     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460     prefs->setBool("/tools/spray/dol", gtk_toggle_action_get_active(act));
4462 static void spray_toggle_doo (GtkToggleAction *act, gpointer ) {
4463     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4464     prefs->setBool("/tools/spray/doo", gtk_toggle_action_get_active(act));
4466 */
4467 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4469     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4470     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4472     {
4473         /* Width */
4474         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4475         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4476         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4477                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4478                                                               "/tools/spray/width", 15,
4479                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4480                                                               1, 100, 1.0, 10.0,
4481                                                               labels, values, G_N_ELEMENTS(labels),
4482                                                               sp_spray_width_value_changed,  1, 0 );
4483         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4484         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4485         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4486     }
4488     {
4489         /* Mean */
4490         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4491         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4492         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4493                                                               _("Mean"), _("Mean:"), _("The mean of the spray action"),
4494                                                               "/tools/spray/mean", 20,
4495                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4496                                                               1, 100, 1.0, 10.0,
4497                                                               labels, values, G_N_ELEMENTS(labels),
4498                                                               sp_spray_mean_value_changed,  1, 0 );
4499         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4500         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4501         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4502     }
4504     {
4505         /* Standard_deviation */
4506         gchar const* labels[] = {_("(minimum standard_deviation)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum standard_deviation)")};
4507         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4508         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4509                                                               _("SD"), _("SD:"), _("The standard deviation of the spray action"),
4510                                                               "/tools/spray/standard_deviation", 20,
4511                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4512                                                               1, 100, 1.0, 10.0,
4513                                                               labels, values, G_N_ELEMENTS(labels),
4514                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4515         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4516         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4517         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4518     }
4520     /* Mode */
4521     {
4522         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4524         GtkTreeIter iter;
4525         gtk_list_store_append( model, &iter );
4526         gtk_list_store_set( model, &iter,
4527                             0, _("Spray with copies"),
4528                             1, _("Spray copies of the initial selection"),
4529                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4530                             -1 );
4532         gtk_list_store_append( model, &iter );
4533         gtk_list_store_set( model, &iter,
4534                             0, _("Spray with clones"),
4535                             1, _("Spray clones of the initial selection"),
4536                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4537                             -1 );
4539         gtk_list_store_append( model, &iter );
4540         gtk_list_store_set( model, &iter,
4541                             0, _("Spray single path"),
4542                             1, _("Spray objects in a single path"),
4543                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4544                             -1 );
4546         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4547         g_object_set( act, "short_label", _("Mode:"), NULL );
4548         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4549         g_object_set_data( holder, "mode_action", act );
4551         ege_select_one_action_set_appearance( act, "full" );
4552         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4553         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4554         ege_select_one_action_set_icon_column( act, 2 );
4555         ege_select_one_action_set_icon_size( act, secondarySize );
4556         ege_select_one_action_set_tooltip_column( act, 1  );
4558         gint mode = prefs->getInt("/tools/spray/mode", 0);
4559         ege_select_one_action_set_active( act, mode );
4560         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4562         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4563     }
4565     {   /* Population */
4566         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4567         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4568         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4569                                                               _("Population"), _("Population:"),
4570                                                               _("This setting adjusts the number of items sprayed"),
4571                                                               "/tools/spray/population", 50,
4572                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4573                                                               1, 100, 1.0, 10.0,
4574                                                               labels, values, G_N_ELEMENTS(labels),
4575                                                               sp_spray_population_value_changed,  1, 0 );
4576         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4577         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4578         g_object_set_data( holder, "spray_population", eact );
4579     }
4581     /* Use Pressure button */
4582     {
4583         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4584                                                       _("Pressure"),
4585                                                       _("Use the pressure of the input device to alter the force of spray action"),
4586                                                       "use_pressure",
4587                                                       Inkscape::ICON_SIZE_DECORATION );
4588         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4589         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4590         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4591     }
4595 //########################
4596 //##     Calligraphy    ##
4597 //########################
4598 static void update_presets_list (GObject *tbl)
4600     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4601     if (g_object_get_data(tbl, "presets_blocked"))
4602         return;
4604     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4605     if (!sel) {
4606         // WTF!? This will cause a segfault if ever reached
4607         //ege_select_one_action_set_active(sel, 0);
4608         return;
4609     }
4611     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4613     int ege_index = 1;
4614     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4615         bool match = true;
4617         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4618         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4619             Glib::ustring entry_name = j->getEntryName();
4620             if (entry_name == "id" || entry_name == "name") continue;
4622             void *widget = g_object_get_data(tbl, entry_name.data());
4623             if (widget) {
4624                 if (GTK_IS_ADJUSTMENT(widget)) {
4625                     double v = j->getDouble();
4626                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4627                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4628                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4629                         match = false;
4630                         break;
4631                     }
4632                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4633                     bool v = j->getBool();
4634                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4635                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4636                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4637                         match = false;
4638                         break;
4639                     }
4640                 }
4641             }
4642         }
4644         if (match) {
4645             // newly added item is at the same index as the
4646             // save command, so we need to change twice for it to take effect
4647             ege_select_one_action_set_active(sel, 0);
4648             ege_select_one_action_set_active(sel, ege_index); // one-based index
4649             return;
4650         }
4651     }
4653     // no match found
4654     ege_select_one_action_set_active(sel, 0);
4657 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4659     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4660     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4661     update_presets_list(tbl);
4664 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4666     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4667     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4668     update_presets_list(tbl);
4671 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4673     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4674     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4675     update_presets_list(tbl);
4678 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4680     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4681     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4682     update_presets_list(tbl);
4685 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4687     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4688     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4689     update_presets_list(tbl);
4692 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4694     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4695     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4696     update_presets_list(tbl);
4699 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4701     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4702     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4703     update_presets_list(tbl);
4706 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4708     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4709     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4710     update_presets_list(tbl);
4713 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4715     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4716     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4717     update_presets_list(tbl);
4720 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4722     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4723     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4724     update_presets_list(tbl);
4727 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4729     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4730     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4731     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4732     update_presets_list(tbl);
4733     if (calligraphy_angle )
4734         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4738 static gchar const *const widget_names[] = {
4739     "width",
4740     "mass",
4741     "wiggle",
4742     "angle",
4743     "thinning",
4744     "tremor",
4745     "flatness",
4746     "cap_rounding",
4747     "usepressure",
4748     "tracebackground",
4749     "usetilt"
4750 };
4753 static void sp_dcc_build_presets_list(GObject *tbl)
4755     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4757     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4758     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4759     gtk_list_store_clear (model);
4761     {
4762         GtkTreeIter iter;
4763         gtk_list_store_append( model, &iter );
4764         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4765     }
4767     // iterate over all presets to populate the list
4768     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4769     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4770     int ii=1;
4772     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4773         GtkTreeIter iter;
4774         Glib::ustring preset_name = prefs->getString(*i + "/name");
4775         gtk_list_store_append( model, &iter );
4776         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4777     }
4779     {
4780         GtkTreeIter iter;
4781         gtk_list_store_append( model, &iter );
4782         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4783         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4784     }
4786     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4788     update_presets_list (tbl);
4791 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4793     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4794     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4795     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4796     if (! desktop) return;
4798     if (g_object_get_data(tbl, "presets_blocked"))
4799         return;
4801     CalligraphicProfileRename::show(desktop);
4802     if ( !CalligraphicProfileRename::applied()) {
4803         // dialog cancelled
4804         update_presets_list (tbl);
4805         return;
4806     }
4807     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4809     if (profile_name.empty()) {
4810         // empty name entered
4811         update_presets_list (tbl);
4812         return;
4813     }
4815     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4817     // If there's a preset with the given name, find it and set save_path appropriately
4818     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4819     int total_presets = presets.size();
4820     int new_index = -1;
4821     Glib::ustring save_path; // profile pref path without a trailing slash
4823     int temp_index = 0;
4824     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4825         Glib::ustring name = prefs->getString(*i + "/name");
4826         if (!name.empty() && profile_name == name) {
4827             new_index = temp_index;
4828             save_path = *i;
4829             break;
4830         }
4831     }
4833     if (new_index == -1) {
4834         // no preset with this name, create
4835         new_index = total_presets + 1;
4836         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4837         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4838         g_free(profile_id);
4839     }
4841     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4842         gchar const *const widget_name = widget_names[i];
4843         void *widget = g_object_get_data(tbl, widget_name);
4844         if (widget) {
4845             if (GTK_IS_ADJUSTMENT(widget)) {
4846                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4847                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4848                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4849             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4850                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4851                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4852                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4853             } else {
4854                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4855             }
4856         } else {
4857             g_warning("Bad key when writing preset: %s\n", widget_name);
4858         }
4859     }
4860     prefs->setString(save_path + "/name", profile_name);
4862     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4863     sp_dcc_build_presets_list (tbl);
4867 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4869     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4871     gint preset_index = ege_select_one_action_get_active( act );
4872     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4873     // even when the preset is not changed. It would be good to replace it with something more
4874     // modern. Index 0 means "No preset", so we don't do anything.
4875     if (preset_index == 0) return;
4877     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4879     if (preset_index == save_presets_index) {
4880         // this is the Save command
4881         sp_dcc_save_profile(NULL, tbl);
4882         return;
4883     }
4885     if (g_object_get_data(tbl, "presets_blocked"))
4886         return;
4888     // preset_index is one-based so we subtract 1
4889     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4890     Glib::ustring preset_path = presets.at(preset_index - 1);
4892     if (!preset_path.empty()) {
4893         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
4895         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4897         // Shouldn't this be std::map?
4898         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4899             Glib::ustring entry_name = i->getEntryName();
4900             if (entry_name == "id" || entry_name == "name") continue;
4901             void *widget = g_object_get_data(tbl, entry_name.data());
4902             if (widget) {
4903                 if (GTK_IS_ADJUSTMENT(widget)) {
4904                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4905                     gtk_adjustment_set_value(adj, i->getDouble());
4906                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4907                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4908                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4909                     gtk_toggle_action_set_active(toggle, i->getBool());
4910                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4911                 } else {
4912                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4913                 }
4914             } else {
4915                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4916             }
4917         }
4918         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4919     }
4923 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4925     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4926     {
4927         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4929         EgeAdjustmentAction* calligraphy_angle = 0;
4931         {
4932         /* Width */
4933         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4934         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4935         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4936                                                               _("Pen Width"), _("Width:"),
4937                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4938                                                               "/tools/calligraphic/width", 15,
4939                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4940                                                               1, 100, 1.0, 10.0,
4941                                                               labels, values, G_N_ELEMENTS(labels),
4942                                                               sp_ddc_width_value_changed,  1, 0 );
4943         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4944         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4945         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4946         }
4948         {
4949         /* Thinning */
4950             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4951             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4952         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4953                                                               _("Stroke Thinning"), _("Thinning:"),
4954                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4955                                                               "/tools/calligraphic/thinning", 10,
4956                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4957                                                               -100, 100, 1, 10.0,
4958                                                               labels, values, G_N_ELEMENTS(labels),
4959                                                               sp_ddc_velthin_value_changed, 1, 0);
4960         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4961         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4962         }
4964         {
4965         /* Angle */
4966         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4967         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4968         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4969                                                               _("Pen Angle"), _("Angle:"),
4970                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4971                                                               "/tools/calligraphic/angle", 30,
4972                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4973                                                               -90.0, 90.0, 1.0, 10.0,
4974                                                               labels, values, G_N_ELEMENTS(labels),
4975                                                               sp_ddc_angle_value_changed, 1, 0 );
4976         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4977         g_object_set_data( holder, "angle_action", eact );
4978         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4979         calligraphy_angle = eact;
4980         }
4982         {
4983         /* Fixation */
4984             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4985         gdouble values[] = {0, 20, 40, 60, 90, 100};
4986         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4987                                                               _("Fixation"), _("Fixation:"),
4988                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4989                                                               "/tools/calligraphic/flatness", 90,
4990                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4991                                                               0.0, 100, 1.0, 10.0,
4992                                                               labels, values, G_N_ELEMENTS(labels),
4993                                                               sp_ddc_flatness_value_changed, 1, 0);
4994         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4995         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4996         }
4998         {
4999         /* Cap Rounding */
5000             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5001         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5002         // TRANSLATORS: "cap" means "end" (both start and finish) here
5003         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5004                                                               _("Cap rounding"), _("Caps:"),
5005                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5006                                                               "/tools/calligraphic/cap_rounding", 0.0,
5007                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5008                                                               0.0, 5.0, 0.01, 0.1,
5009                                                               labels, values, G_N_ELEMENTS(labels),
5010                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5011         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5012         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5013         }
5015         {
5016         /* Tremor */
5017             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5018         gdouble values[] = {0, 10, 20, 40, 60, 100};
5019         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5020                                                               _("Stroke Tremor"), _("Tremor:"),
5021                                                               _("Increase to make strokes rugged and trembling"),
5022                                                               "/tools/calligraphic/tremor", 0.0,
5023                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5024                                                               0.0, 100, 1, 10.0,
5025                                                               labels, values, G_N_ELEMENTS(labels),
5026                                                               sp_ddc_tremor_value_changed, 1, 0);
5028         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5029         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5030         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5031         }
5033         {
5034         /* Wiggle */
5035         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5036         gdouble values[] = {0, 20, 40, 60, 100};
5037         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5038                                                               _("Pen Wiggle"), _("Wiggle:"),
5039                                                               _("Increase to make the pen waver and wiggle"),
5040                                                               "/tools/calligraphic/wiggle", 0.0,
5041                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5042                                                               0.0, 100, 1, 10.0,
5043                                                               labels, values, G_N_ELEMENTS(labels),
5044                                                               sp_ddc_wiggle_value_changed, 1, 0);
5045         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5046         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5047         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5048         }
5050         {
5051         /* Mass */
5052             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5053         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5054         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5055                                                               _("Pen Mass"), _("Mass:"),
5056                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5057                                                               "/tools/calligraphic/mass", 2.0,
5058                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5059                                                               0.0, 100, 1, 10.0,
5060                                                               labels, values, G_N_ELEMENTS(labels),
5061                                                               sp_ddc_mass_value_changed, 1, 0);
5062         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5063         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5064         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5065         }
5068         /* Trace Background button */
5069         {
5070             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5071                                                           _("Trace Background"),
5072                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5073                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5074                                                           Inkscape::ICON_SIZE_DECORATION );
5075             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5076             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5077             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5078             g_object_set_data( holder, "tracebackground", act );
5079         }
5081         /* Use Pressure button */
5082         {
5083             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5084                                                           _("Pressure"),
5085                                                           _("Use the pressure of the input device to alter the width of the pen"),
5086                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5087                                                           Inkscape::ICON_SIZE_DECORATION );
5088             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5089             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5090             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5091             g_object_set_data( holder, "usepressure", act );
5092         }
5094         /* Use Tilt button */
5095         {
5096             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5097                                                           _("Tilt"),
5098                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5099                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5100                                                           Inkscape::ICON_SIZE_DECORATION );
5101             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5102             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5103             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5104             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5105             g_object_set_data( holder, "usetilt", act );
5106         }
5108         /*calligraphic profile */
5109         {
5110             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5111             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5112             ege_select_one_action_set_appearance (act1, "compact");
5113             g_object_set_data (holder, "profile_selector", act1 );
5115             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5117             sp_dcc_build_presets_list (holder);
5119             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5120             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5121         }
5122     }
5126 //########################
5127 //##    Circle / Arc    ##
5128 //########################
5130 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5132     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5133     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5135     if (v1 == 0 && v2 == 0) {
5136         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5137             gtk_action_set_sensitive( ocb, FALSE );
5138             gtk_action_set_sensitive( make_whole, FALSE );
5139         }
5140     } else {
5141         gtk_action_set_sensitive( ocb, TRUE );
5142         gtk_action_set_sensitive( make_whole, TRUE );
5143     }
5146 static void
5147 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5149     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5151     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5152         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5153         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5154     }
5156     // quit if run by the attr_changed listener
5157     if (g_object_get_data( tbl, "freeze" )) {
5158         return;
5159     }
5161     // in turn, prevent listener from responding
5162     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5164     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5166     bool modmade = false;
5167     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5168          items != NULL;
5169          items = items->next)
5170     {
5171         SPItem *item = SP_ITEM(items->data);
5173         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5175             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5176             SPArc *arc = SP_ARC(item);
5178             if (!strcmp(value_name, "start"))
5179                 ge->start = (adj->value * M_PI)/ 180;
5180             else
5181                 ge->end = (adj->value * M_PI)/ 180;
5183             sp_genericellipse_normalize(ge);
5184             ((SPObject *)arc)->updateRepr();
5185             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5187             modmade = true;
5188         }
5189     }
5191     g_free(namespaced_name);
5193     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5195     sp_arctb_sensitivize( tbl, adj->value, other->value );
5197     if (modmade) {
5198         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5199                                    _("Arc: Change start/end"));
5200     }
5202     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5206 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5208     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5211 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5213     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5217 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5219     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5220     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5221         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5222         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5223     }
5225     // quit if run by the attr_changed listener
5226     if (g_object_get_data( tbl, "freeze" )) {
5227         return;
5228     }
5230     // in turn, prevent listener from responding
5231     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5233     bool modmade = false;
5235     if ( ege_select_one_action_get_active(act) != 0 ) {
5236         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5237              items != NULL;
5238              items = items->next)
5239         {
5240             if (SP_IS_ARC((SPItem *) items->data)) {
5241                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5242                 repr->setAttribute("sodipodi:open", "true");
5243                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5244                 modmade = true;
5245             }
5246         }
5247     } else {
5248         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5249              items != NULL;
5250              items = items->next)
5251         {
5252             if (SP_IS_ARC((SPItem *) items->data))    {
5253                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5254                 repr->setAttribute("sodipodi:open", NULL);
5255                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5256                 modmade = true;
5257             }
5258         }
5259     }
5261     if (modmade) {
5262         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5263                                    _("Arc: Change open/closed"));
5264     }
5266     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5269 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5271     GtkAdjustment *adj;
5272     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5273     gtk_adjustment_set_value(adj, 0.0);
5274     gtk_adjustment_value_changed(adj);
5276     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5277     gtk_adjustment_set_value(adj, 0.0);
5278     gtk_adjustment_value_changed(adj);
5280     spinbutton_defocus( GTK_OBJECT(obj) );
5283 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5284                                       gchar const */*old_value*/, gchar const */*new_value*/,
5285                                       bool /*is_interactive*/, gpointer data)
5287     GObject *tbl = G_OBJECT(data);
5289     // quit if run by the _changed callbacks
5290     if (g_object_get_data( tbl, "freeze" )) {
5291         return;
5292     }
5294     // in turn, prevent callbacks from responding
5295     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5297     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5298     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5300     GtkAdjustment *adj1,*adj2;
5301     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5302     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5303     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5304     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5306     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5308     char const *openstr = NULL;
5309     openstr = repr->attribute("sodipodi:open");
5310     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5312     if (openstr) {
5313         ege_select_one_action_set_active( ocb, 1 );
5314     } else {
5315         ege_select_one_action_set_active( ocb, 0 );
5316     }
5318     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5321 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5322     NULL, /* child_added */
5323     NULL, /* child_removed */
5324     arc_tb_event_attr_changed,
5325     NULL, /* content_changed */
5326     NULL  /* order_changed */
5327 };
5330 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5332     int n_selected = 0;
5333     Inkscape::XML::Node *repr = NULL;
5335     purge_repr_listener( tbl, tbl );
5337     for (GSList const *items = selection->itemList();
5338          items != NULL;
5339          items = items->next)
5340     {
5341         if (SP_IS_ARC((SPItem *) items->data)) {
5342             n_selected++;
5343             repr = SP_OBJECT_REPR((SPItem *) items->data);
5344         }
5345     }
5347     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5349     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5350     if (n_selected == 0) {
5351         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5352     } else if (n_selected == 1) {
5353         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5354         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5356         if (repr) {
5357             g_object_set_data( tbl, "repr", repr );
5358             Inkscape::GC::anchor(repr);
5359             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5360             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5361         }
5362     } else {
5363         // FIXME: implement averaging of all parameters for multiple selected
5364         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5365         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5366         sp_arctb_sensitivize( tbl, 1, 0 );
5367     }
5371 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5373     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5375     EgeAdjustmentAction* eact = 0;
5376     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5379     {
5380         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5381         ege_output_action_set_use_markup( act, TRUE );
5382         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5383         g_object_set_data( holder, "mode_action", act );
5384     }
5386     /* Start */
5387     {
5388         eact = create_adjustment_action( "ArcStartAction",
5389                                          _("Start"), _("Start:"),
5390                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5391                                          "/tools/shapes/arc/start", 0.0,
5392                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5393                                          -360.0, 360.0, 1.0, 10.0,
5394                                          0, 0, 0,
5395                                          sp_arctb_start_value_changed);
5396         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5397     }
5399     /* End */
5400     {
5401         eact = create_adjustment_action( "ArcEndAction",
5402                                          _("End"), _("End:"),
5403                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5404                                          "/tools/shapes/arc/end", 0.0,
5405                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5406                                          -360.0, 360.0, 1.0, 10.0,
5407                                          0, 0, 0,
5408                                          sp_arctb_end_value_changed);
5409         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5410     }
5412     /* Segments / Pie checkbox */
5413     {
5414         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5416         GtkTreeIter iter;
5417         gtk_list_store_append( model, &iter );
5418         gtk_list_store_set( model, &iter,
5419                             0, _("Closed arc"),
5420                             1, _("Switch to segment (closed shape with two radii)"),
5421                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5422                             -1 );
5424         gtk_list_store_append( model, &iter );
5425         gtk_list_store_set( model, &iter,
5426                             0, _("Open Arc"),
5427                             1, _("Switch to arc (unclosed shape)"),
5428                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5429                             -1 );
5431         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5432         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5433         g_object_set_data( holder, "open_action", act );
5435         ege_select_one_action_set_appearance( act, "full" );
5436         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5437         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5438         ege_select_one_action_set_icon_column( act, 2 );
5439         ege_select_one_action_set_icon_size( act, secondarySize );
5440         ege_select_one_action_set_tooltip_column( act, 1  );
5442         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5443         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5444         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5445     }
5447     /* Make Whole */
5448     {
5449         InkAction* inky = ink_action_new( "ArcResetAction",
5450                                           _("Make whole"),
5451                                           _("Make the shape a whole ellipse, not arc or segment"),
5452                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5453                                           secondarySize );
5454         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5455         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5456         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5457         g_object_set_data( holder, "make_whole", inky );
5458     }
5460     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5461     // sensitivize make whole and open checkbox
5462     {
5463         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5464         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5465         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5466     }
5469     sigc::connection *connection = new sigc::connection(
5470         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5471         );
5472     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5473     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5479 // toggle button callbacks and updaters
5481 //########################
5482 //##      Dropper       ##
5483 //########################
5485 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5486     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5487     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5488     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5489     if ( set_action ) {
5490         if ( gtk_toggle_action_get_active( act ) ) {
5491             gtk_action_set_sensitive( set_action, TRUE );
5492         } else {
5493             gtk_action_set_sensitive( set_action, FALSE );
5494         }
5495     }
5497     spinbutton_defocus(GTK_OBJECT(tbl));
5500 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5501     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5502     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5503     spinbutton_defocus(GTK_OBJECT(tbl));
5507 /**
5508  * Dropper auxiliary toolbar construction and setup.
5509  *
5510  * TODO: Would like to add swatch of current color.
5511  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5512  *       can drag and drop places. Will provide a nice mixing palette.
5513  */
5514 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5516     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5517     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5519     {
5520         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5521         ege_output_action_set_use_markup( act, TRUE );
5522         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5523     }
5525     {
5526         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5527                                                       _("Pick opacity"),
5528                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5529                                                       NULL,
5530                                                       Inkscape::ICON_SIZE_DECORATION );
5531         g_object_set( act, "short_label", _("Pick"), NULL );
5532         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5533         g_object_set_data( holder, "pick_action", act );
5534         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5535         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5536     }
5538     {
5539         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5540                                                       _("Assign opacity"),
5541                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5542                                                       NULL,
5543                                                       Inkscape::ICON_SIZE_DECORATION );
5544         g_object_set( act, "short_label", _("Assign"), NULL );
5545         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5546         g_object_set_data( holder, "set_action", act );
5547         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5548         // make sure it's disabled if we're not picking alpha
5549         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5550         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5551     }
5555 //########################
5556 //##      LPETool       ##
5557 //########################
5559 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5561 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5562 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5564     using namespace Inkscape::LivePathEffect;
5566     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5567     SPEventContext *ec = desktop->event_context;
5568     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5569         return;
5570     }
5572     // only take action if run by the attr_changed listener
5573     if (!g_object_get_data(tbl, "freeze")) {
5574         // in turn, prevent listener from responding
5575         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5577         gint mode = ege_select_one_action_get_active(act);
5578         EffectType type = lpesubtools[mode].type;
5580         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5581         bool success = lpetool_try_construction(lc, type);
5582         if (success) {
5583             // since the construction was already performed, we set the state back to inactive
5584             ege_select_one_action_set_active(act, 0);
5585             mode = 0;
5586         } else {
5587             // switch to the chosen subtool
5588             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5589         }
5591         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5592             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5593             prefs->setInt( "/tools/lpetool/mode", mode );
5594         }
5596         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5597     }
5600 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5602     SPEventContext *ec = selection->desktop()->event_context;
5603     if (!SP_IS_LPETOOL_CONTEXT(ec))
5604         return;
5606     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5609 void
5610 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5612     using namespace Inkscape::LivePathEffect;
5613     SPEventContext *ec = selection->desktop()->event_context;
5614     if (!SP_IS_LPETOOL_CONTEXT(ec))
5615         return;
5616     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5618     lpetool_delete_measuring_items(lc);
5619     lpetool_create_measuring_items(lc, selection);
5621     // activate line segment combo box if a single item with LPELineSegment is selected
5622     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5623     SPItem *item = selection->singleItem();
5624     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5625         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5626         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5627         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5628             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5629             g_object_set_data(tbl, "currentlpe", lpe);
5630             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5631             gtk_action_set_sensitive(w, TRUE);
5632             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5633         } else {
5634             g_object_set_data(tbl, "currentlpe", NULL);
5635             g_object_set_data(tbl, "currentlpeitem", NULL);
5636             gtk_action_set_sensitive(w, FALSE);
5637         }
5638     } else {
5639         g_object_set_data(tbl, "currentlpe", NULL);
5640         g_object_set_data(tbl, "currentlpeitem", NULL);
5641         gtk_action_set_sensitive(w, FALSE);
5642     }
5645 static void
5646 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5647     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5648     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5650     bool show = gtk_toggle_action_get_active( act );
5651     prefs->setBool("/tools/lpetool/show_bbox",  show);
5653     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5654         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5655         lpetool_context_reset_limiting_bbox(lc);
5656     }
5659 static void
5660 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5661     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5662     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5663         return;
5665     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5666     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5667     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5668         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5669         bool show = gtk_toggle_action_get_active( act );
5670         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5671         lpetool_show_measuring_info(lc, show);
5672         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5673     }
5676 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5677     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5678     SPUnit const *unit = tracker->getActiveUnit();
5679     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5680     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5682     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5683     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5684         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5685         lpetool_delete_measuring_items(lc);
5686         lpetool_create_measuring_items(lc);
5687     }
5690 static void
5691 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5692     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5693     Inkscape::Selection *selection = desktop->selection;
5695     Geom::OptRect bbox = selection->bounds();
5697     if (bbox) {
5698         Geom::Point A(bbox->min());
5699         Geom::Point B(bbox->max());
5701         A *= desktop->doc2dt();
5702         B *= desktop->doc2dt();
5704         // TODO: should we provide a way to store points in prefs?
5705         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5706         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5707         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5708         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5709         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5711         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5712     }
5714     gtk_toggle_action_set_active(act, false);
5717 static void
5718 sp_line_segment_build_list(GObject *tbl)
5720     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5722     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5723     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5724     gtk_list_store_clear (model);
5726     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5727     {
5728         GtkTreeIter iter;
5729         gtk_list_store_append( model, &iter );
5730         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5731         gtk_list_store_append( model, &iter );
5732         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5733         gtk_list_store_append( model, &iter );
5734         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5735         gtk_list_store_append( model, &iter );
5736         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5737     }
5739     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5742 static void
5743 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5744     using namespace Inkscape::LivePathEffect;
5746     // quit if run by the attr_changed listener
5747     if (g_object_get_data(tbl, "freeze")) {
5748         return;
5749     }
5751     // in turn, prevent listener from responding
5752     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5754     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5755     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5756     if (lpeitem) {
5757         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5758         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5759         sp_lpe_item_update_patheffect(lpeitem, true, true);
5760     }
5762     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5765 static void
5766 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5767     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5769     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5770         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5771     }
5772     gtk_toggle_action_set_active(act, false);
5775 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5777     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5778     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5779     g_object_set_data(holder, "tracker", tracker);
5780     SPUnit const *unit = tracker->getActiveUnit();
5782     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5783     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5785     /** Automatically create a list of LPEs that get added to the toolbar **/
5786     {
5787         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5789         GtkTreeIter iter;
5791         // the first toggle button represents the state that no subtool is active (remove this when
5792         // this can be modeled by EgeSelectOneAction or some other action)
5793         gtk_list_store_append( model, &iter );
5794         gtk_list_store_set( model, &iter,
5795                             0, _("All inactive"),
5796                             1, _("No geometric tool is active"),
5797                             2, "draw-geometry-inactive",
5798                             -1 );
5800         Inkscape::LivePathEffect::EffectType type;
5801         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5802             type =  lpesubtools[i].type;
5803             gtk_list_store_append( model, &iter );
5804             gtk_list_store_set( model, &iter,
5805                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5806                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5807                                 2, lpesubtools[i].icon_name,
5808                                 -1 );
5809         }
5811         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5812         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5813         g_object_set_data( holder, "lpetool_mode_action", act );
5815         ege_select_one_action_set_appearance( act, "full" );
5816         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5817         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5818         ege_select_one_action_set_icon_column( act, 2 );
5819         ege_select_one_action_set_tooltip_column( act, 1  );
5821         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5822         ege_select_one_action_set_active( act, lpeToolMode );
5823         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5824     }
5826     /* Show limiting bounding box */
5827     {
5828         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5829                                                       _("Show limiting bounding box"),
5830                                                       _("Show bounding box (used to cut infinite lines)"),
5831                                                       "show-bounding-box",
5832                                                       Inkscape::ICON_SIZE_DECORATION );
5833         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5834         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5835         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5836     }
5838     /* Set limiting bounding box to bbox of current selection */
5839     {
5840         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5841                                                       _("Get limiting bounding box from selection"),
5842                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5843                                                       "draw-geometry-set-bounding-box",
5844                                                       Inkscape::ICON_SIZE_DECORATION );
5845         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5846         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5847         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5848     }
5851     /* Combo box to choose line segment type */
5852     {
5853         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5854         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5855         ege_select_one_action_set_appearance (act, "compact");
5856         g_object_set_data (holder, "lpetool_line_segment_action", act );
5858         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5860         sp_line_segment_build_list (holder);
5862         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5863         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5864         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5865     }
5867     /* Display measuring info for selected items */
5868     {
5869         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5870                                                       _("Display measuring info"),
5871                                                       _("Display measuring info for selected items"),
5872                                                       "draw-geometry-show-measuring-info",
5873                                                       Inkscape::ICON_SIZE_DECORATION );
5874         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5875         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5876         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5877     }
5879     // add the units menu
5880     {
5881         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5882         gtk_action_group_add_action( mainActions, act );
5883         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5884         g_object_set_data(holder, "lpetool_units_action", act);
5885         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5886     }
5888     /* Open LPE dialog (to adapt parameters numerically) */
5889     {
5890         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5891                                                       _("Open LPE dialog"),
5892                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5893                                                       "dialog-geometry",
5894                                                       Inkscape::ICON_SIZE_DECORATION );
5895         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5896         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5897         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5898     }
5900     //watch selection
5901     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5903     sigc::connection *c_selection_modified =
5904         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5905                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5906     pool->add_connection ("selection-modified", c_selection_modified);
5908     sigc::connection *c_selection_changed =
5909         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5910                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5911     pool->add_connection ("selection-changed", c_selection_changed);
5914 //########################
5915 //##       Eraser       ##
5916 //########################
5918 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5920     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5921     prefs->setDouble( "/tools/eraser/width", adj->value );
5922     update_presets_list(tbl);
5925 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5927     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5928     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5929     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5930         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5931         prefs->setBool( "/tools/eraser/mode", eraserMode );
5932     }
5934     // only take action if run by the attr_changed listener
5935     if (!g_object_get_data( tbl, "freeze" )) {
5936         // in turn, prevent listener from responding
5937         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5939         if ( eraserMode != 0 ) {
5940         } else {
5941         }
5942         // TODO finish implementation
5944         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5945     }
5948 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5950     {
5951         /* Width */
5952         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5953         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5954         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5955                                                               _("Pen Width"), _("Width:"),
5956                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5957                                                               "/tools/eraser/width", 15,
5958                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5959                                                               1, 100, 1.0, 10.0,
5960                                                               labels, values, G_N_ELEMENTS(labels),
5961                                                               sp_erc_width_value_changed, 1, 0);
5962         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5963         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5964         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5965     }
5967     {
5968         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5970         GtkTreeIter iter;
5971         gtk_list_store_append( model, &iter );
5972         gtk_list_store_set( model, &iter,
5973                             0, _("Delete"),
5974                             1, _("Delete objects touched by the eraser"),
5975                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5976                             -1 );
5978         gtk_list_store_append( model, &iter );
5979         gtk_list_store_set( model, &iter,
5980                             0, _("Cut"),
5981                             1, _("Cut out from objects"),
5982                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5983                             -1 );
5985         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5986         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5987         g_object_set_data( holder, "eraser_mode_action", act );
5989         ege_select_one_action_set_appearance( act, "full" );
5990         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5991         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5992         ege_select_one_action_set_icon_column( act, 2 );
5993         ege_select_one_action_set_tooltip_column( act, 1  );
5995         /// @todo Convert to boolean?
5996         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5997         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5998         ege_select_one_action_set_active( act, eraserMode );
5999         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6000     }
6004 //########################
6005 //##    Text Toolbox    ##
6006 //########################
6007 /*
6008 static void
6009 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6011     //Call back for letter sizing spinbutton
6014 static void
6015 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6017     //Call back for line height spinbutton
6020 static void
6021 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6023     //Call back for horizontal kerning spinbutton
6026 static void
6027 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6029     //Call back for vertical kerning spinbutton
6032 static void
6033 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6035     //Call back for letter rotation spinbutton
6036 }*/
6038 namespace {
6040 void
6041 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6043     // quit if run by the _changed callbacks
6044     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6045         return;
6046     }
6048     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6050     SPStyle *query =
6051         sp_style_new (SP_ACTIVE_DOCUMENT);
6053     int result_family =
6054         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6056     int result_style =
6057         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6059     int result_numbers =
6060         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6062     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6064     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6065     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6066         // there are no texts in selection, read from prefs
6068             sp_style_read_from_prefs(query, "/tools/text");
6070             if (g_object_get_data(tbl, "text_style_from_prefs")) {
6071             // do not reset the toolbar style from prefs if we already did it last time
6072             sp_style_unref(query);
6073             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6074             return;
6075         }
6076         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6077     } else {
6078         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6079     }
6081     if (query->text)
6082     {
6083         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6084             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6085             gtk_entry_set_text (GTK_ENTRY (entry), "");
6087         } else if (query->text->font_specification.value || query->text->font_family.value) {
6089             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6090             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6092             // Get the font that corresponds
6093             Glib::ustring familyName;
6095             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6096             if (font) {
6097                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6098                 font->Unref();
6099                 font = NULL;
6100             }
6102             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6104             Gtk::TreeIter iter;
6105             try {
6106                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6107                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6108                 iter = model->get_iter(path);
6109             } catch (...) {
6110                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6111                 sp_style_unref(query);
6112                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6113                 return;
6114             }
6116             combo->set_active (iter);
6117         }
6119         //Size
6120         {
6121             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6122             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6123             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6124             g_free(str);
6125         }
6127         //Anchor
6128         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6129         {
6130             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6131             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6132             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6133             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6134         }
6135         else
6136         {
6137             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6138             {
6139                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6140                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6141                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6142                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6143             }
6144             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6145             {
6146                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6147                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6148                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6149                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6150             }
6151             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6152             {
6153                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6154                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6155                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6156                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6157             }
6158         }
6160         //Style
6161         {
6162             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6164             gboolean active = gtk_toggle_button_get_active (button);
6165             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));
6167             if (active != check)
6168             {
6169                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6170                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6171                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6172             }
6173         }
6175         {
6176             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6178             gboolean active = gtk_toggle_button_get_active (button);
6179             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6181             if (active != check)
6182             {
6183                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6184                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6185                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6186             }
6187         }
6189         //Orientation
6190         //locking both buttons, changing one affect all group (both)
6191         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6192         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6194         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6195         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6197         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6198         {
6199             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6200         }
6201         else
6202         {
6203             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6204         }
6205         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6206         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6207     }
6209     sp_style_unref(query);
6211     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6214 void
6215 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6217     sp_text_toolbox_selection_changed (selection, tbl);
6220 void
6221 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6223     sp_text_toolbox_selection_changed (NULL, tbl);
6226 void
6227 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6228                                 GObject             *tbl)
6230     // quit if run by the _changed callbacks
6231     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6232         return;
6233     }
6235     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6237     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6238     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6239     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6241     //g_print ("family changed to: %s\n", family);
6243     SPStyle *query =
6244         sp_style_new (SP_ACTIVE_DOCUMENT);
6246     int result_fontspec =
6247         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6249     SPCSSAttr *css = sp_repr_css_attr_new ();
6251     // First try to get the font spec from the stored value
6252     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6254     if (fontSpec.empty()) {
6255         // Construct a new font specification if it does not yet exist
6256         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6257         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6258         fontFromStyle->Unref();
6259     }
6261     if (!fontSpec.empty()) {
6263         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6265         if (!newFontSpec.empty()) {
6267             if (fontSpec != newFontSpec) {
6269                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6271                 if (font) {
6272                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6274                     // Set all the these just in case they were altered when finding the best
6275                     // match for the new family and old style...
6277                     gchar c[256];
6279                     font->Family(c, 256);
6281                     sp_repr_css_set_property (css, "font-family", c);
6283                     font->Attribute( "weight", c, 256);
6284                     sp_repr_css_set_property (css, "font-weight", c);
6286                     font->Attribute("style", c, 256);
6287                     sp_repr_css_set_property (css, "font-style", c);
6289                     font->Attribute("stretch", c, 256);
6290                     sp_repr_css_set_property (css, "font-stretch", c);
6292                     font->Attribute("variant", c, 256);
6293                     sp_repr_css_set_property (css, "font-variant", c);
6295                     font->Unref();
6296                 }
6297             }
6299         } else {
6300             // If the old font on selection (or default) was not existing on the system,
6301             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6302             // setting the family reported by the family chooser.
6304             //g_print ("fallback setting family: %s\n", family);
6305             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6306             sp_repr_css_set_property (css, "font-family", family);
6307         }
6308     }
6310     // If querying returned nothing, set the default style of the tool (for new texts)
6311     if (result_fontspec == QUERY_STYLE_NOTHING)
6312     {
6313         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6314         prefs->mergeStyle("/tools/text/style", css);
6315         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6316     }
6317     else
6318     {
6319         sp_desktop_set_style (desktop, css, true, true);
6320     }
6322     sp_style_unref(query);
6324     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6325                                    _("Text: Change font family"));
6326     sp_repr_css_attr_unref (css);
6328     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6330     // unfreeze
6331     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6333     // focus to canvas
6334     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6338 void
6339 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6340                                    gpointer          data)
6342     if (g_object_get_data (G_OBJECT (button), "block")) return;
6343     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6344     int prop = GPOINTER_TO_INT(data);
6346     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6347     SPCSSAttr *css = sp_repr_css_attr_new ();
6349     switch (prop)
6350     {
6351         case 0:
6352         {
6353             sp_repr_css_set_property (css, "text-anchor", "start");
6354             sp_repr_css_set_property (css, "text-align", "start");
6355             break;
6356         }
6357         case 1:
6358         {
6359             sp_repr_css_set_property (css, "text-anchor", "middle");
6360             sp_repr_css_set_property (css, "text-align", "center");
6361             break;
6362         }
6364         case 2:
6365         {
6366             sp_repr_css_set_property (css, "text-anchor", "end");
6367             sp_repr_css_set_property (css, "text-align", "end");
6368             break;
6369         }
6371         case 3:
6372         {
6373             sp_repr_css_set_property (css, "text-anchor", "start");
6374             sp_repr_css_set_property (css, "text-align", "justify");
6375             break;
6376         }
6377     }
6379     SPStyle *query =
6380         sp_style_new (SP_ACTIVE_DOCUMENT);
6381     int result_numbers =
6382         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6384     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6385     if (result_numbers == QUERY_STYLE_NOTHING)
6386     {
6387         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6388         prefs->mergeStyle("/tools/text/style", css);
6389     }
6391     sp_style_unref(query);
6393     sp_desktop_set_style (desktop, css, true, true);
6394     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6395                                    _("Text: Change alignment"));
6396     sp_repr_css_attr_unref (css);
6398     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6401 void
6402 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6403                                gpointer          data)
6405     if (g_object_get_data (G_OBJECT (button), "block")) return;
6407     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6408     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6409     int          prop       = GPOINTER_TO_INT(data);
6410     bool         active     = gtk_toggle_button_get_active (button);
6412     SPStyle *query =
6413         sp_style_new (SP_ACTIVE_DOCUMENT);
6415     int result_fontspec =
6416         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6418     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6419     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6420     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6422     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6423     Glib::ustring newFontSpec = "";
6425     if (fontSpec.empty()) {
6426         // Construct a new font specification if it does not yet exist
6427         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6428         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6429         fontFromStyle->Unref();
6430     }
6432     bool nochange = true;
6433     switch (prop)
6434     {
6435         case 0:
6436         {
6437             if (!fontSpec.empty()) {
6438                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6439                 if (!newFontSpec.empty()) {
6440                     // Don't even set the bold if the font didn't exist on the system
6441                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6442                     nochange = false;
6443                 }
6444             }
6445             // set or reset the button according
6446             if(nochange) {
6447                 gboolean check = gtk_toggle_button_get_active (button);
6449                 if (active != check)
6450                 {
6451                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6452                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6453                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6454                 }
6455             }
6457             break;
6458         }
6460         case 1:
6461         {
6462             if (!fontSpec.empty()) {
6463                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6464                 if (!newFontSpec.empty()) {
6465                     // Don't even set the italic if the font didn't exist on the system
6466                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6467                     nochange = false;
6468                 }
6469             }
6470             if(nochange) {
6471                 gboolean check = gtk_toggle_button_get_active (button);
6473                 if (active != check)
6474                 {
6475                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6476                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6477                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6478                 }
6479             }
6480             break;
6481         }
6482     }
6484     if (!newFontSpec.empty()) {
6485         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6486     }
6488     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6489     if (result_fontspec == QUERY_STYLE_NOTHING)
6490     {
6491         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6492         prefs->mergeStyle("/tools/text/style", css);
6493     }
6495     sp_style_unref(query);
6497     sp_desktop_set_style (desktop, css, true, true);
6498     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6499                                    _("Text: Change font style"));
6500     sp_repr_css_attr_unref (css);
6502     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6505 void
6506 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6507                                      gpointer         data)
6509     if (g_object_get_data (G_OBJECT (button), "block")) {
6510         return;
6511     }
6513     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6514     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6515     int          prop       = GPOINTER_TO_INT(data);
6517     switch (prop)
6518     {
6519         case 0:
6520         {
6521             sp_repr_css_set_property (css, "writing-mode", "lr");
6522             break;
6523         }
6525         case 1:
6526         {
6527             sp_repr_css_set_property (css, "writing-mode", "tb");
6528             break;
6529         }
6530     }
6532     SPStyle *query =
6533         sp_style_new (SP_ACTIVE_DOCUMENT);
6534     int result_numbers =
6535         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6537     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6538     if (result_numbers == QUERY_STYLE_NOTHING)
6539     {
6540         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6541         prefs->mergeStyle("/tools/text/style", css);
6542     }
6544     sp_desktop_set_style (desktop, css, true, true);
6545     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6546                                    _("Text: Change orientation"));
6547     sp_repr_css_attr_unref (css);
6549     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6552 gboolean
6553 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6555     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6556     if (!desktop) return FALSE;
6558     switch (get_group0_keyval (event)) {
6559         case GDK_KP_Enter: // chosen
6560         case GDK_Return:
6561             // unfreeze and update, which will defocus
6562             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6563             sp_text_toolbox_family_changed (NULL, tbl);
6564             return TRUE; // I consumed the event
6565             break;
6566         case GDK_Escape:
6567             // defocus
6568             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6569             return TRUE; // I consumed the event
6570             break;
6571     }
6572     return FALSE;
6575 gboolean
6576 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6578     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6579     if (!desktop) return FALSE;
6581     switch (get_group0_keyval (event)) {
6582         case GDK_KP_Enter:
6583         case GDK_Return:
6584         case GDK_Escape: // defocus
6585             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6586             return TRUE; // I consumed the event
6587             break;
6588         case GDK_w:
6589         case GDK_W:
6590             if (event->state & GDK_CONTROL_MASK) {
6591                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6592                 return TRUE; // I consumed the event
6593             }
6594             break;
6595     }
6596     return FALSE;
6600 void
6601 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6602                                GObject     *tbl)
6604      // quit if run by the _changed callbacks
6605     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6606         return;
6607     }
6609     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6611    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6613     // If this is not from selecting a size in the list (in which case get_active will give the
6614     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6615     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6616     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6617    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6618         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6619         return;
6620    }
6622     gdouble value = -1;
6623     {
6624         gchar *endptr;
6625         gchar *const text = gtk_combo_box_get_active_text(cbox);
6626         if (text) {
6627             value = g_strtod(text, &endptr);
6628             if (endptr == text) {  // Conversion failed, non-numeric input.
6629                 value = -1;
6630             }
6631             g_free(text);
6632         }
6633     }
6634     if (value <= 0) {
6635         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6636         return; // could not parse value
6637     }
6639     SPCSSAttr *css = sp_repr_css_attr_new ();
6640     Inkscape::CSSOStringStream osfs;
6641     osfs << value;
6642     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6644     SPStyle *query =
6645         sp_style_new (SP_ACTIVE_DOCUMENT);
6646     int result_numbers =
6647         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6649     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6650     if (result_numbers == QUERY_STYLE_NOTHING)
6651     {
6652         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6653         prefs->mergeStyle("/tools/text/style", css);
6654     }
6656     sp_style_unref(query);
6658     sp_desktop_set_style (desktop, css, true, true);
6659     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6660                                    _("Text: Change font size"));
6661     sp_repr_css_attr_unref (css);
6663     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6665     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6668 gboolean
6669 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6671     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6672     if (!desktop) return FALSE;
6674     if (!g_object_get_data (tbl, "esc-pressed")) {
6675         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6676         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6677         sp_text_toolbox_size_changed (cbox, tbl);
6678         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6679     }
6680     return FALSE; // I consumed the event
6684 gboolean
6685 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6687     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6688     if (!desktop) return FALSE;
6690     switch (get_group0_keyval (event)) {
6691         case GDK_Escape: // defocus
6692             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6693             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6694             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6695             return TRUE; // I consumed the event
6696             break;
6697         case GDK_Return: // defocus
6698         case GDK_KP_Enter:
6699             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6700             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6701             sp_text_toolbox_size_changed (cbox, tbl);
6702             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6703             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6704             return TRUE; // I consumed the event
6705             break;
6706     }
6707     return FALSE;
6710 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6711 // does not work!
6712 gboolean
6713 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6714                                  GdkEventFocus    */*event*/,
6715                                  GObject          *tbl)
6717     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6718     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6719     return FALSE;
6722 gboolean
6723 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6724                                  GdkEventFocus    */*event*/,
6725                                  GObject          *tbl)
6727     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6728     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6729     return FALSE;
6732 void
6733 cell_data_func  (GtkCellLayout */*cell_layout*/,
6734                  GtkCellRenderer   *cell,
6735                  GtkTreeModel      *tree_model,
6736                  GtkTreeIter       *iter,
6737                  gpointer           /*data*/)
6739     gchar *family;
6740     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6741     gchar *const family_escaped = g_markup_escape_text(family, -1);
6743     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6744     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6745     if (show_sample) {
6747         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6748         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6750     std::stringstream markup;
6751     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6752            << family_escaped << "'>" << sample_escaped << "</span>";
6753     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6755         g_free(sample_escaped);
6756     } else {
6757         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6758     }
6760     g_free(family);
6761     g_free(family_escaped);
6764 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6765                                                 GtkTreeModel       *model,
6766                                                 GtkTreeIter        *iter,
6767                                                 GObject            *tbl)
6769     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6770     // press Enter again after choosing a completion)
6771     gchar *family = 0;
6772     gtk_tree_model_get(model, iter, 0, &family, -1);
6774     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6775     gtk_entry_set_text (GTK_ENTRY (entry), family);
6777     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6778     sp_text_toolbox_family_changed (NULL, tbl);
6779     return TRUE;
6783 static void
6784 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6785     GtkEntry *entry;
6786     GtkEntryCompletion *completion;
6787     GtkTreeModel *model;
6789     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6790     completion = gtk_entry_completion_new();
6791     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6792     gtk_entry_completion_set_model(completion, model);
6793     gtk_entry_completion_set_text_column(completion, 0);
6794     gtk_entry_completion_set_inline_completion(completion, FALSE);
6795     gtk_entry_completion_set_inline_selection(completion, FALSE);
6796     gtk_entry_completion_set_popup_completion(completion, TRUE);
6797     gtk_entry_set_completion(entry, completion);
6799     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6801     g_object_unref(completion);
6804 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6805                                       void */*property*/,
6806                                       GObject *tbl)
6808   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6810   gboolean shown;
6811   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6812   if (shown) {
6813          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6814          //g_print("POP: notify: SHOWN\n");
6815   } else {
6816          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6818          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6819          // find out if the drop down was closed by Enter and if so, manually update (only
6820          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6821          GdkEvent *ev = gtk_get_current_event();
6822          if (ev) {
6823              //g_print ("ev type: %d\n", ev->type);
6824              if (ev->type == GDK_KEY_PRESS) {
6825                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6826                      case GDK_KP_Enter: // chosen
6827                      case GDK_Return:
6828                      {
6829                          // make sure the chosen one is inserted into the entry
6830                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6831                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6832                          GtkTreeIter iter;
6833                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6834                          if (has_active) {
6835                              gchar *family;
6836                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6837                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6838                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6839                          }
6841                          // update
6842                          sp_text_toolbox_family_changed (NULL, tbl);
6843                          break;
6844                      }
6845                  }
6846              }
6847          }
6849          // regardless of whether we updated, defocus the widget
6850          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6851          if (desktop)
6852              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6853          //g_print("POP: notify: HIDDEN\n");
6854   }
6857 GtkWidget*
6858 sp_text_toolbox_new (SPDesktop *desktop)
6860     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6861     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6863     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6864     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6866     GtkTooltips *tt = gtk_tooltips_new();
6868     ////////////Family
6869     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6870     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6872     gtk_rc_parse_string (
6873        "style \"dropdown-as-list-style\"\n"
6874        "{\n"
6875        "    GtkComboBox::appears-as-list = 1\n"
6876        "}\n"
6877        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6878     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6879     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6881     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6883     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6885     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6886     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6888     // expand the field a bit so as to view more of the previews in the drop-down
6889     GtkRequisition req;
6890     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6891     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6893     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6894     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6896     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6897     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6898              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6899     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6900     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6901     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6903     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6904     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6906     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6907     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6908     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6909     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6911     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6912     GtkWidget *box = gtk_event_box_new ();
6913     gtk_container_add (GTK_CONTAINER (box), image);
6914     gtk_toolbar_append_widget( tbl, box, "", "");
6915     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6916     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6917     gtk_widget_hide (GTK_WIDGET (box));
6918     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6920     ////////////Size
6921     gchar const *const sizes[] = {
6922         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6923         "16", "18", "20", "22", "24", "28",
6924         "32", "36", "40", "48", "56", "64", "72", "144"
6925     };
6927     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6928     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6929         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6930     }
6931     gtk_widget_set_size_request (cbox, 80, -1);
6932     gtk_toolbar_append_widget( tbl, cbox, "", "");
6933     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6934     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6935     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6936     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6938     ////////////Text anchor
6939     GtkWidget *group   = gtk_radio_button_new (NULL);
6940     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6941     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6943     // left
6944     GtkWidget *rbutton = group;
6945     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6946     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6947     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6949     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6950     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6951     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6952     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6954     // center
6955     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (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_CENTER, 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-middle", rbutton);
6962     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6963     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6965     // right
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_RIGHT, 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-end", rbutton);
6973     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6974     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6976     // fill
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_FILL, 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-fill", rbutton);
6984     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6985     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6987     gtk_toolbar_append_widget( tbl, row, "", "");
6989     //spacer
6990     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6992     ////////////Text style
6993     row = gtk_hbox_new (FALSE, 4);
6995     // bold
6996     rbutton = gtk_toggle_button_new ();
6997     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6998     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6999     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7000     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7002     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7003     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7004     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7006     // italic
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_ITALIC, secondarySize));
7010     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7011     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7013     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7014     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7015     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7017     gtk_toolbar_append_widget( tbl, row, "", "");
7019     //spacer
7020     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7022     // Text orientation
7023     group   = gtk_radio_button_new (NULL);
7024     row     = gtk_hbox_new (FALSE, 4);
7025     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7027     // horizontal
7028     rbutton = group;
7029     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7030     gtk_container_add           (GTK_CONTAINER (rbutton),
7031                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7032     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7033     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7035     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7036     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7037     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7039     // vertical
7040     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7041     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7042     gtk_container_add           (GTK_CONTAINER (rbutton),
7043                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7044     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7045     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7047     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7048     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7049     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7050     gtk_toolbar_append_widget( tbl, row, "", "" );
7053     //watch selection
7054     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7056     sigc::connection *c_selection_changed =
7057         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7058                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7059     pool->add_connection ("selection-changed", c_selection_changed);
7061     sigc::connection *c_selection_modified =
7062         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7063                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7064     pool->add_connection ("selection-modified", c_selection_modified);
7066     sigc::connection *c_subselection_changed =
7067         new sigc::connection (desktop->connectToolSubselectionChanged
7068                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7069     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7071     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7074     gtk_widget_show_all( GTK_WIDGET(tbl) );
7076     return GTK_WIDGET(tbl);
7077 } // end of sp_text_toolbox_new()
7079 }//<unnamed> namespace
7082 //#########################
7083 //##      Connector      ##
7084 //#########################
7086 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7088     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7089     prefs->setBool("/tools/connector/mode",
7090                 gtk_toggle_action_get_active( act ));
7093 static void sp_connector_path_set_avoid(void)
7095     cc_selection_set_avoid(true);
7099 static void sp_connector_path_set_ignore(void)
7101     cc_selection_set_avoid(false);
7104 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7106     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7107     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7108     SPDocument *doc = sp_desktop_document(desktop);
7110     if (!sp_document_get_undo_sensitive(doc))
7111     {
7112         return;
7113     }
7116     // quit if run by the _changed callbacks
7117     if (g_object_get_data( tbl, "freeze" )) {
7118         return;
7119     }
7121     // in turn, prevent callbacks from responding
7122     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7124     bool is_orthog = gtk_toggle_action_get_active( act );
7125     gchar orthog_str[] = "orthogonal";
7126     gchar polyline_str[] = "polyline";
7127     gchar *value = is_orthog ? orthog_str : polyline_str ;
7129     bool modmade = false;
7130     GSList *l = (GSList *) selection->itemList();
7131     while (l) {
7132         SPItem *item = (SPItem *) l->data;
7134         if (cc_item_is_connector(item)) {
7135             sp_object_setAttribute(item, "inkscape:connector-type",
7136                     value, false);
7137             item->avoidRef->handleSettingChange();
7138             modmade = true;
7139         }
7140         l = l->next;
7141     }
7143     if (!modmade)
7144     {
7145         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7146         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7147     }
7149     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7150             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7152     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7155 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7157     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7158     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7159     SPDocument *doc = sp_desktop_document(desktop);
7161     if (!sp_document_get_undo_sensitive(doc))
7162     {
7163         return;
7164     }
7167     // quit if run by the _changed callbacks
7168     if (g_object_get_data( tbl, "freeze" )) {
7169         return;
7170     }
7172     // in turn, prevent callbacks from responding
7173     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7175     gdouble newValue = gtk_adjustment_get_value(adj);
7176     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7177     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7179     bool modmade = false;
7180     GSList *l = (GSList *) selection->itemList();
7181     while (l) {
7182         SPItem *item = (SPItem *) l->data;
7184         if (cc_item_is_connector(item)) {
7185             sp_object_setAttribute(item, "inkscape:connector-curvature",
7186                     value, false);
7187             item->avoidRef->handleSettingChange();
7188             modmade = true;
7189         }
7190         l = l->next;
7191     }
7193     if (!modmade)
7194     {
7195         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7196         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7197     }
7199     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7200             _("Change connector curvature"));
7202     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7206 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7208     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7209     SPDocument *doc = sp_desktop_document(desktop);
7211     if (!sp_document_get_undo_sensitive(doc))
7212     {
7213         return;
7214     }
7216     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7218     if ( !repr->attribute("inkscape:connector-spacing") &&
7219             ( adj->value == defaultConnSpacing )) {
7220         // Don't need to update the repr if the attribute doesn't
7221         // exist and it is being set to the default value -- as will
7222         // happen at startup.
7223         return;
7224     }
7226     // quit if run by the attr_changed listener
7227     if (g_object_get_data( tbl, "freeze" )) {
7228         return;
7229     }
7231     // in turn, prevent listener from responding
7232     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7234     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7235     SP_OBJECT(desktop->namedview)->updateRepr();
7237     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7238     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7239         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7240         Geom::Matrix m = Geom::identity();
7241         avoid_item_move(&m, item);
7242     }
7244     if (items) {
7245         g_slist_free(items);
7246     }
7248     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7249             _("Change connector spacing"));
7251     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7254 static void sp_connector_graph_layout(void)
7256     if (!SP_ACTIVE_DESKTOP) return;
7257     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7259     // hack for clones, see comment in align-and-distribute.cpp
7260     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7261     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7263     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7265     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7267     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7270 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7272     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7273     prefs->setBool("/tools/connector/directedlayout",
7274                 gtk_toggle_action_get_active( act ));
7277 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7279     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7280     prefs->setBool("/tools/connector/avoidoverlaplayout",
7281                 gtk_toggle_action_get_active( act ));
7285 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7287     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7288     prefs->setDouble("/tools/connector/length", adj->value);
7291 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7292                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7293                                             bool /*is_interactive*/, gpointer data)
7295     GtkWidget *tbl = GTK_WIDGET(data);
7297     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7298         return;
7299     }
7300     if (strcmp(name, "inkscape:connector-spacing") == 0)
7301     {
7302         GtkAdjustment *adj = (GtkAdjustment*)
7303                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7304         gdouble spacing = defaultConnSpacing;
7305         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7307         gtk_adjustment_set_value(adj, spacing);
7308         gtk_adjustment_value_changed(adj);
7309     }
7311     spinbutton_defocus(GTK_OBJECT(tbl));
7314 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7316     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7317     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7319     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7320         cc_create_connection_point(cc);
7323 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7325     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7326     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7328     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7329         cc_remove_connection_point(cc);
7332 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7333     NULL, /* child_added */
7334     NULL, /* child_removed */
7335     connector_tb_event_attr_changed,
7336     NULL, /* content_changed */
7337     NULL  /* order_changed */
7338 };
7340 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7342     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7343     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7344     SPItem *item = selection->singleItem();
7345     if (SP_IS_PATH(item))
7346     {
7347         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7348         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7349         gtk_toggle_action_set_active(act, is_orthog);
7350         gtk_adjustment_set_value(adj, curvature);
7351     }
7355 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7357     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7358     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7360     // Editing mode toggle button
7361     {
7362         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7363                                                       _("EditMode"),
7364                                                       _("Switch between connection point editing and connector drawing mode"),
7365                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7366                                                       Inkscape::ICON_SIZE_DECORATION );
7367         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7369         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7370         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7371         g_object_set_data( holder, "mode", act );
7372         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7373     }
7376     {
7377         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7378                                           _("Avoid"),
7379                                           _("Make connectors avoid selected objects"),
7380                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7381                                           secondarySize );
7382         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7383         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7384     }
7386     {
7387         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7388                                           _("Ignore"),
7389                                           _("Make connectors ignore selected objects"),
7390                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7391                                           secondarySize );
7392         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7393         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7394     }
7396     // Orthogonal connectors toggle button
7397     {
7398         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7399                                                       _("Orthogonal"),
7400                                                       _("Make connector orthogonal or polyline"),
7401                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7402                                                       Inkscape::ICON_SIZE_DECORATION );
7403         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7405         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7406         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7407         g_object_set_data( holder, "orthogonal", act );
7408         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7409     }
7411     EgeAdjustmentAction* eact = 0;
7412     // Curvature spinbox
7413     eact = create_adjustment_action( "ConnectorCurvatureAction",
7414                                     _("Connector Curvature"), _("Curvature:"),
7415                                     _("The amount of connectors curvature"),
7416                                     "/tools/connector/curvature", defaultConnCurvature,
7417                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7418                                     0, 100, 1.0, 10.0,
7419                                     0, 0, 0,
7420                                     connector_curvature_changed, 1, 0 );
7421     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7423     // Spacing spinbox
7424     eact = create_adjustment_action( "ConnectorSpacingAction",
7425                                     _("Connector Spacing"), _("Spacing:"),
7426                                     _("The amount of space left around objects by auto-routing connectors"),
7427                                     "/tools/connector/spacing", defaultConnSpacing,
7428                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7429                                     0, 100, 1.0, 10.0,
7430                                     0, 0, 0,
7431                                     connector_spacing_changed, 1, 0 );
7432     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7434     // Graph (connector network) layout
7435     {
7436         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7437                                           _("Graph"),
7438                                           _("Nicely arrange selected connector network"),
7439                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7440                                           secondarySize );
7441         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7442         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7443     }
7445     // Default connector length spinbox
7446     eact = create_adjustment_action( "ConnectorLengthAction",
7447                                      _("Connector Length"), _("Length:"),
7448                                      _("Ideal length for connectors when layout is applied"),
7449                                      "/tools/connector/length", 100,
7450                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7451                                      10, 1000, 10.0, 100.0,
7452                                      0, 0, 0,
7453                                      connector_length_changed, 1, 0 );
7454     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7457     // Directed edges toggle button
7458     {
7459         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7460                                                       _("Downwards"),
7461                                                       _("Make connectors with end-markers (arrows) point downwards"),
7462                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7463                                                       Inkscape::ICON_SIZE_DECORATION );
7464         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7466         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7467         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7469         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7470         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7471         );
7472     }
7474     // Avoid overlaps toggle button
7475     {
7476         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7477                                                       _("Remove overlaps"),
7478                                                       _("Do not allow overlapping shapes"),
7479                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7480                                                       Inkscape::ICON_SIZE_DECORATION );
7481         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7483         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7484         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7486         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7487     }
7490     // New connection point button
7491     {
7492         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7493                                           _("New connection point"),
7494                                           _("Add a new connection point to the currently selected item"),
7495                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7496                                           secondarySize );
7497         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7498         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7499     }
7501     // Remove selected connection point button
7503     {
7504         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7505                                           _("Remove connection point"),
7506                                           _("Remove the currently selected connection point"),
7507                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7508                                           secondarySize );
7509         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7510         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7511     }
7514     // Code to watch for changes to the connector-spacing attribute in
7515     // the XML.
7516     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7517     g_assert(repr != NULL);
7519     purge_repr_listener( holder, holder );
7521     if (repr) {
7522         g_object_set_data( holder, "repr", repr );
7523         Inkscape::GC::anchor(repr);
7524         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7525         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7526     }
7527 } // end of sp_connector_toolbox_prep()
7530 //#########################
7531 //##     Paintbucket     ##
7532 //#########################
7534 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7536     gint channels = ege_select_one_action_get_active( act );
7537     flood_channels_set_channels( channels );
7540 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7542     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7543     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7546 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7548     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7549     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7552 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7554     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7555     SPUnit const *unit = tracker->getActiveUnit();
7556     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7558     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7559     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7562 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7564     // FIXME: make defaults settable via Inkscape Options
7565     struct KeyValue {
7566         char const *key;
7567         double value;
7568     } const key_values[] = {
7569         {"threshold", 15},
7570         {"offset", 0.0}
7571     };
7573     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7574         KeyValue const &kv = key_values[i];
7575         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7576         if ( adj ) {
7577             gtk_adjustment_set_value(adj, kv.value);
7578         }
7579     }
7581     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7582     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7583     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7584     ege_select_one_action_set_active( autogap_action, 0 );
7587 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7589     EgeAdjustmentAction* eact = 0;
7590     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7592     {
7593         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7595         GList* items = 0;
7596         gint count = 0;
7597         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7598         {
7599             GtkTreeIter iter;
7600             gtk_list_store_append( model, &iter );
7601             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7602             count++;
7603         }
7604         g_list_free( items );
7605         items = 0;
7606         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7607         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7608         ege_select_one_action_set_appearance( act1, "compact" );
7609         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7610         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7611         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7612         g_object_set_data( holder, "channels_action", act1 );
7613     }
7615     // Spacing spinbox
7616     {
7617         eact = create_adjustment_action(
7618             "ThresholdAction",
7619             _("Fill Threshold"), _("Threshold:"),
7620             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7621             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7622             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7623             0, 0, 0,
7624             paintbucket_threshold_changed, 1, 0 );
7626         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7627         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7628     }
7630     // Create the units menu.
7631     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7632     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7633     if (!stored_unit.empty())
7634         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7635     g_object_set_data( holder, "tracker", tracker );
7636     {
7637         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7638         gtk_action_group_add_action( mainActions, act );
7639     }
7641     // Offset spinbox
7642     {
7643         eact = create_adjustment_action(
7644             "OffsetAction",
7645             _("Grow/shrink by"), _("Grow/shrink by:"),
7646             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7647             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7648             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7649             0, 0, 0,
7650             paintbucket_offset_changed, 1, 2);
7651         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7653         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7654     }
7656     /* Auto Gap */
7657     {
7658         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7660         GList* items = 0;
7661         gint count = 0;
7662         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7663         {
7664             GtkTreeIter iter;
7665             gtk_list_store_append( model, &iter );
7666             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7667             count++;
7668         }
7669         g_list_free( items );
7670         items = 0;
7671         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7672         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7673         ege_select_one_action_set_appearance( act2, "compact" );
7674         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7675         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7676         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7677         g_object_set_data( holder, "autogap_action", act2 );
7678     }
7680     /* Reset */
7681     {
7682         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7683                                           _("Defaults"),
7684                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7685                                           GTK_STOCK_CLEAR );
7686         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7687         gtk_action_group_add_action( mainActions, act );
7688         gtk_action_set_sensitive( act, TRUE );
7689     }
7693 /*
7694   Local Variables:
7695   mode:c++
7696   c-file-style:"stroustrup"
7697   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7698   indent-tabs-mode:nil
7699   fill-column:99
7700   End:
7701 */
7702 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :