Code

f5f1094f01980fa400efac2814d600313564c0f1
[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 static void sp_spray_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4413     //Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4414     //prefs->setDouble( "/tools/spray/force", adj->value * 0.01 );
4417 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4419     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4420     prefs->setDouble( "/tools/spray/mean", adj->value );
4423 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4425     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4426     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4429 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4431     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4432     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4435 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4437     int mode = ege_select_one_action_get_active( act );
4438     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439     prefs->setInt("/tools/spray/mode", mode);
4442 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4444     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4445     prefs->setDouble( "/tools/spray/population", adj->value );
4448 /*static void spray_toggle_doh (GtkToggleAction *act, gpointer ) {
4449     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4450     prefs->setBool("/tools/spray/doh", gtk_toggle_action_get_active(act));
4452 static void spray_toggle_dos (GtkToggleAction *act, gpointer ) {
4453     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4454     prefs->setBool("/tools/spray/dos", gtk_toggle_action_get_active(act));
4456 static void spray_toggle_dol (GtkToggleAction *act, gpointer ) {
4457     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458     prefs->setBool("/tools/spray/dol", gtk_toggle_action_get_active(act));
4460 static void spray_toggle_doo (GtkToggleAction *act, gpointer ) {
4461     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4462     prefs->setBool("/tools/spray/doo", gtk_toggle_action_get_active(act));
4464 */
4465 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4467     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4468     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4470     {
4471         /* Width */
4472         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4473         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4474         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4475                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4476                                                               "/tools/spray/width", 15,
4477                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4478                                                               1, 100, 1.0, 10.0,
4479                                                               labels, values, G_N_ELEMENTS(labels),
4480                                                               sp_spray_width_value_changed,  1, 0 );
4481         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4482         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4483         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4484     }
4486     {
4487         /* Mean */
4488         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4489         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4490         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4491                                                               _("Mean"), _("Mean:"), _("The mean of the spray action"),
4492                                                               "/tools/spray/mean", 20,
4493                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4494                                                               1, 100, 1.0, 10.0,
4495                                                               labels, values, G_N_ELEMENTS(labels),
4496                                                               sp_spray_mean_value_changed,  1, 0 );
4497         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4498         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4499         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4500     }
4502     {
4503         /* Standard_deviation */
4504         gchar const* labels[] = {_("(minimum standard_deviation)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum standard_deviation)")};
4505         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4506         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4507                                                               _("SD"), _("SD:"), _("The standard deviation of the spray action"),
4508                                                               "/tools/spray/standard_deviation", 20,
4509                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4510                                                               1, 100, 1.0, 10.0,
4511                                                               labels, values, G_N_ELEMENTS(labels),
4512                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4513         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4514         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4515         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4516     }
4518     /* Mode */
4519     {
4520         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4522         GtkTreeIter iter;
4523         gtk_list_store_append( model, &iter );
4524         gtk_list_store_set( model, &iter,
4525                             0, _("Spray with copies"),
4526                             1, _("Spray copies of the initial selection"),
4527                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4528                             -1 );
4530         gtk_list_store_append( model, &iter );
4531         gtk_list_store_set( model, &iter,
4532                             0, _("Spray with clones"),
4533                             1, _("Spray clones of the initial selection"),
4534                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4535                             -1 );
4537         gtk_list_store_append( model, &iter );
4538         gtk_list_store_set( model, &iter,
4539                             0, _("Spray single path"),
4540                             1, _("Spray objects in a single path"),
4541                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4542                             -1 );
4544         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4545         g_object_set( act, "short_label", _("Mode:"), NULL );
4546         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4547         g_object_set_data( holder, "mode_action", act );
4549         ege_select_one_action_set_appearance( act, "full" );
4550         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4551         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4552         ege_select_one_action_set_icon_column( act, 2 );
4553         ege_select_one_action_set_icon_size( act, secondarySize );
4554         ege_select_one_action_set_tooltip_column( act, 1  );
4556         gint mode = prefs->getInt("/tools/spray/mode", 0);
4557         ege_select_one_action_set_active( act, mode );
4558         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4560         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4561     }
4563     {   /* Population */
4564         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4565         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4566         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4567                                                               _("Population"), _("Population:"),
4568                                                               _("This setting adjusts the number of items sprayed"),
4569                                                               "/tools/spray/population", 50,
4570                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4571                                                               1, 100, 1.0, 10.0,
4572                                                               labels, values, G_N_ELEMENTS(labels),
4573                                                               sp_spray_population_value_changed,  1, 0 );
4574         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4575         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4576         g_object_set_data( holder, "spray_population", eact );
4577     }
4579     /* Use Pressure button */
4580     {
4581         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4582                                                       _("Pressure"),
4583                                                       _("Use the pressure of the input device to alter the force of spray action"),
4584                                                       "use_pressure",
4585                                                       Inkscape::ICON_SIZE_DECORATION );
4586         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4587         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4588         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4589     }
4593 //########################
4594 //##     Calligraphy    ##
4595 //########################
4596 static void update_presets_list (GObject *tbl)
4598     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4599     if (g_object_get_data(tbl, "presets_blocked"))
4600         return;
4602     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4603     if (!sel) {
4604         // WTF!? This will cause a segfault if ever reached
4605         //ege_select_one_action_set_active(sel, 0);
4606         return;
4607     }
4609     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4611     int ege_index = 1;
4612     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4613         bool match = true;
4615         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4616         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4617             Glib::ustring entry_name = j->getEntryName();
4618             if (entry_name == "id" || entry_name == "name") continue;
4620             void *widget = g_object_get_data(tbl, entry_name.data());
4621             if (widget) {
4622                 if (GTK_IS_ADJUSTMENT(widget)) {
4623                     double v = j->getDouble();
4624                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4625                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4626                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4627                         match = false;
4628                         break;
4629                     }
4630                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4631                     bool v = j->getBool();
4632                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4633                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4634                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4635                         match = false;
4636                         break;
4637                     }
4638                 }
4639             }
4640         }
4642         if (match) {
4643             // newly added item is at the same index as the
4644             // save command, so we need to change twice for it to take effect
4645             ege_select_one_action_set_active(sel, 0);
4646             ege_select_one_action_set_active(sel, ege_index); // one-based index
4647             return;
4648         }
4649     }
4651     // no match found
4652     ege_select_one_action_set_active(sel, 0);
4655 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4657     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4658     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4659     update_presets_list(tbl);
4662 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4664     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4665     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4666     update_presets_list(tbl);
4669 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4671     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4672     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4673     update_presets_list(tbl);
4676 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4678     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4679     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4680     update_presets_list(tbl);
4683 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4685     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4686     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4687     update_presets_list(tbl);
4690 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4692     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4693     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4694     update_presets_list(tbl);
4697 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4699     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4700     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4701     update_presets_list(tbl);
4704 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4706     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4707     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4708     update_presets_list(tbl);
4711 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4713     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4714     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4715     update_presets_list(tbl);
4718 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4720     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4721     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4722     update_presets_list(tbl);
4725 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4727     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4728     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4729     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4730     update_presets_list(tbl);
4731     if (calligraphy_angle )
4732         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4736 static gchar const *const widget_names[] = {
4737     "width",
4738     "mass",
4739     "wiggle",
4740     "angle",
4741     "thinning",
4742     "tremor",
4743     "flatness",
4744     "cap_rounding",
4745     "usepressure",
4746     "tracebackground",
4747     "usetilt"
4748 };
4751 static void sp_dcc_build_presets_list(GObject *tbl)
4753     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4755     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4756     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4757     gtk_list_store_clear (model);
4759     {
4760         GtkTreeIter iter;
4761         gtk_list_store_append( model, &iter );
4762         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4763     }
4765     // iterate over all presets to populate the list
4766     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4767     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4768     int ii=1;
4770     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4771         GtkTreeIter iter;
4772         Glib::ustring preset_name = prefs->getString(*i + "/name");
4773         gtk_list_store_append( model, &iter );
4774         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4775     }
4777     {
4778         GtkTreeIter iter;
4779         gtk_list_store_append( model, &iter );
4780         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4781         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4782     }
4784     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4786     update_presets_list (tbl);
4789 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4791     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4792     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4793     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4794     if (! desktop) return;
4796     if (g_object_get_data(tbl, "presets_blocked"))
4797         return;
4799     CalligraphicProfileRename::show(desktop);
4800     if ( !CalligraphicProfileRename::applied()) {
4801         // dialog cancelled
4802         update_presets_list (tbl);
4803         return;
4804     }
4805     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4807     if (profile_name.empty()) {
4808         // empty name entered
4809         update_presets_list (tbl);
4810         return;
4811     }
4813     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4815     // If there's a preset with the given name, find it and set save_path appropriately
4816     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4817     int total_presets = presets.size();
4818     int new_index = -1;
4819     Glib::ustring save_path; // profile pref path without a trailing slash
4821     int temp_index = 0;
4822     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4823         Glib::ustring name = prefs->getString(*i + "/name");
4824         if (!name.empty() && profile_name == name) {
4825             new_index = temp_index;
4826             save_path = *i;
4827             break;
4828         }
4829     }
4831     if (new_index == -1) {
4832         // no preset with this name, create
4833         new_index = total_presets + 1;
4834         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4835         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4836         g_free(profile_id);
4837     }
4839     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4840         gchar const *const widget_name = widget_names[i];
4841         void *widget = g_object_get_data(tbl, widget_name);
4842         if (widget) {
4843             if (GTK_IS_ADJUSTMENT(widget)) {
4844                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4845                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4846                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4847             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4848                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4849                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4850                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4851             } else {
4852                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4853             }
4854         } else {
4855             g_warning("Bad key when writing preset: %s\n", widget_name);
4856         }
4857     }
4858     prefs->setString(save_path + "/name", profile_name);
4860     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4861     sp_dcc_build_presets_list (tbl);
4865 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4867     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4869     gint preset_index = ege_select_one_action_get_active( act );
4870     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4871     // even when the preset is not changed. It would be good to replace it with something more
4872     // modern. Index 0 means "No preset", so we don't do anything.
4873     if (preset_index == 0) return;
4875     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4877     if (preset_index == save_presets_index) {
4878         // this is the Save command
4879         sp_dcc_save_profile(NULL, tbl);
4880         return;
4881     }
4883     if (g_object_get_data(tbl, "presets_blocked"))
4884         return;
4886     // preset_index is one-based so we subtract 1
4887     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4888     Glib::ustring preset_path = presets.at(preset_index - 1);
4890     if (!preset_path.empty()) {
4891         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
4893         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4895         // Shouldn't this be std::map?
4896         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4897             Glib::ustring entry_name = i->getEntryName();
4898             if (entry_name == "id" || entry_name == "name") continue;
4899             void *widget = g_object_get_data(tbl, entry_name.data());
4900             if (widget) {
4901                 if (GTK_IS_ADJUSTMENT(widget)) {
4902                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4903                     gtk_adjustment_set_value(adj, i->getDouble());
4904                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4905                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4906                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4907                     gtk_toggle_action_set_active(toggle, i->getBool());
4908                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4909                 } else {
4910                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4911                 }
4912             } else {
4913                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4914             }
4915         }
4916         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4917     }
4921 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4923     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4924     {
4925         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4927         EgeAdjustmentAction* calligraphy_angle = 0;
4929         {
4930         /* Width */
4931         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4932         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4933         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4934                                                               _("Pen Width"), _("Width:"),
4935                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4936                                                               "/tools/calligraphic/width", 15,
4937                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4938                                                               1, 100, 1.0, 10.0,
4939                                                               labels, values, G_N_ELEMENTS(labels),
4940                                                               sp_ddc_width_value_changed,  1, 0 );
4941         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4942         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4943         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4944         }
4946         {
4947         /* Thinning */
4948             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4949             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4950         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4951                                                               _("Stroke Thinning"), _("Thinning:"),
4952                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4953                                                               "/tools/calligraphic/thinning", 10,
4954                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4955                                                               -100, 100, 1, 10.0,
4956                                                               labels, values, G_N_ELEMENTS(labels),
4957                                                               sp_ddc_velthin_value_changed, 1, 0);
4958         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4959         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4960         }
4962         {
4963         /* Angle */
4964         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4965         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4966         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4967                                                               _("Pen Angle"), _("Angle:"),
4968                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4969                                                               "/tools/calligraphic/angle", 30,
4970                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4971                                                               -90.0, 90.0, 1.0, 10.0,
4972                                                               labels, values, G_N_ELEMENTS(labels),
4973                                                               sp_ddc_angle_value_changed, 1, 0 );
4974         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4975         g_object_set_data( holder, "angle_action", eact );
4976         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4977         calligraphy_angle = eact;
4978         }
4980         {
4981         /* Fixation */
4982             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4983         gdouble values[] = {0, 20, 40, 60, 90, 100};
4984         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4985                                                               _("Fixation"), _("Fixation:"),
4986                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4987                                                               "/tools/calligraphic/flatness", 90,
4988                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4989                                                               0.0, 100, 1.0, 10.0,
4990                                                               labels, values, G_N_ELEMENTS(labels),
4991                                                               sp_ddc_flatness_value_changed, 1, 0);
4992         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4993         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4994         }
4996         {
4997         /* Cap Rounding */
4998             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4999         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5000         // TRANSLATORS: "cap" means "end" (both start and finish) here
5001         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5002                                                               _("Cap rounding"), _("Caps:"),
5003                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5004                                                               "/tools/calligraphic/cap_rounding", 0.0,
5005                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5006                                                               0.0, 5.0, 0.01, 0.1,
5007                                                               labels, values, G_N_ELEMENTS(labels),
5008                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5009         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5010         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5011         }
5013         {
5014         /* Tremor */
5015             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5016         gdouble values[] = {0, 10, 20, 40, 60, 100};
5017         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5018                                                               _("Stroke Tremor"), _("Tremor:"),
5019                                                               _("Increase to make strokes rugged and trembling"),
5020                                                               "/tools/calligraphic/tremor", 0.0,
5021                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5022                                                               0.0, 100, 1, 10.0,
5023                                                               labels, values, G_N_ELEMENTS(labels),
5024                                                               sp_ddc_tremor_value_changed, 1, 0);
5026         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5027         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5028         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5029         }
5031         {
5032         /* Wiggle */
5033         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5034         gdouble values[] = {0, 20, 40, 60, 100};
5035         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5036                                                               _("Pen Wiggle"), _("Wiggle:"),
5037                                                               _("Increase to make the pen waver and wiggle"),
5038                                                               "/tools/calligraphic/wiggle", 0.0,
5039                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5040                                                               0.0, 100, 1, 10.0,
5041                                                               labels, values, G_N_ELEMENTS(labels),
5042                                                               sp_ddc_wiggle_value_changed, 1, 0);
5043         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5044         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5045         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5046         }
5048         {
5049         /* Mass */
5050             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5051         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5052         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5053                                                               _("Pen Mass"), _("Mass:"),
5054                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5055                                                               "/tools/calligraphic/mass", 2.0,
5056                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5057                                                               0.0, 100, 1, 10.0,
5058                                                               labels, values, G_N_ELEMENTS(labels),
5059                                                               sp_ddc_mass_value_changed, 1, 0);
5060         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5061         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5062         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5063         }
5066         /* Trace Background button */
5067         {
5068             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5069                                                           _("Trace Background"),
5070                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5071                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5072                                                           Inkscape::ICON_SIZE_DECORATION );
5073             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5074             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5075             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5076             g_object_set_data( holder, "tracebackground", act );
5077         }
5079         /* Use Pressure button */
5080         {
5081             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5082                                                           _("Pressure"),
5083                                                           _("Use the pressure of the input device to alter the width of the pen"),
5084                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5085                                                           Inkscape::ICON_SIZE_DECORATION );
5086             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5087             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5088             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5089             g_object_set_data( holder, "usepressure", act );
5090         }
5092         /* Use Tilt button */
5093         {
5094             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5095                                                           _("Tilt"),
5096                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5097                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5098                                                           Inkscape::ICON_SIZE_DECORATION );
5099             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5100             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5101             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5102             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5103             g_object_set_data( holder, "usetilt", act );
5104         }
5106         /*calligraphic profile */
5107         {
5108             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5109             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5110             ege_select_one_action_set_appearance (act1, "compact");
5111             g_object_set_data (holder, "profile_selector", act1 );
5113             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5115             sp_dcc_build_presets_list (holder);
5117             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5118             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5119         }
5120     }
5124 //########################
5125 //##    Circle / Arc    ##
5126 //########################
5128 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5130     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5131     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5133     if (v1 == 0 && v2 == 0) {
5134         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5135             gtk_action_set_sensitive( ocb, FALSE );
5136             gtk_action_set_sensitive( make_whole, FALSE );
5137         }
5138     } else {
5139         gtk_action_set_sensitive( ocb, TRUE );
5140         gtk_action_set_sensitive( make_whole, TRUE );
5141     }
5144 static void
5145 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5147     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5149     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5150         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5151         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5152     }
5154     // quit if run by the attr_changed listener
5155     if (g_object_get_data( tbl, "freeze" )) {
5156         return;
5157     }
5159     // in turn, prevent listener from responding
5160     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5162     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5164     bool modmade = false;
5165     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5166          items != NULL;
5167          items = items->next)
5168     {
5169         SPItem *item = SP_ITEM(items->data);
5171         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5173             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5174             SPArc *arc = SP_ARC(item);
5176             if (!strcmp(value_name, "start"))
5177                 ge->start = (adj->value * M_PI)/ 180;
5178             else
5179                 ge->end = (adj->value * M_PI)/ 180;
5181             sp_genericellipse_normalize(ge);
5182             ((SPObject *)arc)->updateRepr();
5183             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5185             modmade = true;
5186         }
5187     }
5189     g_free(namespaced_name);
5191     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5193     sp_arctb_sensitivize( tbl, adj->value, other->value );
5195     if (modmade) {
5196         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5197                                    _("Arc: Change start/end"));
5198     }
5200     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5204 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5206     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5209 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5211     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5215 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5217     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5218     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5219         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5220         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5221     }
5223     // quit if run by the attr_changed listener
5224     if (g_object_get_data( tbl, "freeze" )) {
5225         return;
5226     }
5228     // in turn, prevent listener from responding
5229     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5231     bool modmade = false;
5233     if ( ege_select_one_action_get_active(act) != 0 ) {
5234         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5235              items != NULL;
5236              items = items->next)
5237         {
5238             if (SP_IS_ARC((SPItem *) items->data)) {
5239                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5240                 repr->setAttribute("sodipodi:open", "true");
5241                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5242                 modmade = true;
5243             }
5244         }
5245     } else {
5246         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5247              items != NULL;
5248              items = items->next)
5249         {
5250             if (SP_IS_ARC((SPItem *) items->data))    {
5251                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5252                 repr->setAttribute("sodipodi:open", NULL);
5253                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5254                 modmade = true;
5255             }
5256         }
5257     }
5259     if (modmade) {
5260         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5261                                    _("Arc: Change open/closed"));
5262     }
5264     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5267 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5269     GtkAdjustment *adj;
5270     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5271     gtk_adjustment_set_value(adj, 0.0);
5272     gtk_adjustment_value_changed(adj);
5274     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5275     gtk_adjustment_set_value(adj, 0.0);
5276     gtk_adjustment_value_changed(adj);
5278     spinbutton_defocus( GTK_OBJECT(obj) );
5281 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5282                                       gchar const */*old_value*/, gchar const */*new_value*/,
5283                                       bool /*is_interactive*/, gpointer data)
5285     GObject *tbl = G_OBJECT(data);
5287     // quit if run by the _changed callbacks
5288     if (g_object_get_data( tbl, "freeze" )) {
5289         return;
5290     }
5292     // in turn, prevent callbacks from responding
5293     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5295     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5296     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5298     GtkAdjustment *adj1,*adj2;
5299     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5300     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5301     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5302     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5304     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5306     char const *openstr = NULL;
5307     openstr = repr->attribute("sodipodi:open");
5308     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5310     if (openstr) {
5311         ege_select_one_action_set_active( ocb, 1 );
5312     } else {
5313         ege_select_one_action_set_active( ocb, 0 );
5314     }
5316     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5319 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5320     NULL, /* child_added */
5321     NULL, /* child_removed */
5322     arc_tb_event_attr_changed,
5323     NULL, /* content_changed */
5324     NULL  /* order_changed */
5325 };
5328 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5330     int n_selected = 0;
5331     Inkscape::XML::Node *repr = NULL;
5333     purge_repr_listener( tbl, tbl );
5335     for (GSList const *items = selection->itemList();
5336          items != NULL;
5337          items = items->next)
5338     {
5339         if (SP_IS_ARC((SPItem *) items->data)) {
5340             n_selected++;
5341             repr = SP_OBJECT_REPR((SPItem *) items->data);
5342         }
5343     }
5345     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5347     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5348     if (n_selected == 0) {
5349         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5350     } else if (n_selected == 1) {
5351         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5352         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5354         if (repr) {
5355             g_object_set_data( tbl, "repr", repr );
5356             Inkscape::GC::anchor(repr);
5357             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5358             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5359         }
5360     } else {
5361         // FIXME: implement averaging of all parameters for multiple selected
5362         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5363         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5364         sp_arctb_sensitivize( tbl, 1, 0 );
5365     }
5369 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5371     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5373     EgeAdjustmentAction* eact = 0;
5374     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5377     {
5378         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5379         ege_output_action_set_use_markup( act, TRUE );
5380         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5381         g_object_set_data( holder, "mode_action", act );
5382     }
5384     /* Start */
5385     {
5386         eact = create_adjustment_action( "ArcStartAction",
5387                                          _("Start"), _("Start:"),
5388                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5389                                          "/tools/shapes/arc/start", 0.0,
5390                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5391                                          -360.0, 360.0, 1.0, 10.0,
5392                                          0, 0, 0,
5393                                          sp_arctb_start_value_changed);
5394         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5395     }
5397     /* End */
5398     {
5399         eact = create_adjustment_action( "ArcEndAction",
5400                                          _("End"), _("End:"),
5401                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5402                                          "/tools/shapes/arc/end", 0.0,
5403                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5404                                          -360.0, 360.0, 1.0, 10.0,
5405                                          0, 0, 0,
5406                                          sp_arctb_end_value_changed);
5407         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5408     }
5410     /* Segments / Pie checkbox */
5411     {
5412         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5414         GtkTreeIter iter;
5415         gtk_list_store_append( model, &iter );
5416         gtk_list_store_set( model, &iter,
5417                             0, _("Closed arc"),
5418                             1, _("Switch to segment (closed shape with two radii)"),
5419                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5420                             -1 );
5422         gtk_list_store_append( model, &iter );
5423         gtk_list_store_set( model, &iter,
5424                             0, _("Open Arc"),
5425                             1, _("Switch to arc (unclosed shape)"),
5426                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5427                             -1 );
5429         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5430         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5431         g_object_set_data( holder, "open_action", act );
5433         ege_select_one_action_set_appearance( act, "full" );
5434         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5435         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5436         ege_select_one_action_set_icon_column( act, 2 );
5437         ege_select_one_action_set_icon_size( act, secondarySize );
5438         ege_select_one_action_set_tooltip_column( act, 1  );
5440         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5441         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5442         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5443     }
5445     /* Make Whole */
5446     {
5447         InkAction* inky = ink_action_new( "ArcResetAction",
5448                                           _("Make whole"),
5449                                           _("Make the shape a whole ellipse, not arc or segment"),
5450                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5451                                           secondarySize );
5452         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5453         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5454         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5455         g_object_set_data( holder, "make_whole", inky );
5456     }
5458     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5459     // sensitivize make whole and open checkbox
5460     {
5461         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5462         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5463         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5464     }
5467     sigc::connection *connection = new sigc::connection(
5468         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5469         );
5470     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5471     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5477 // toggle button callbacks and updaters
5479 //########################
5480 //##      Dropper       ##
5481 //########################
5483 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5484     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5485     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5486     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5487     if ( set_action ) {
5488         if ( gtk_toggle_action_get_active( act ) ) {
5489             gtk_action_set_sensitive( set_action, TRUE );
5490         } else {
5491             gtk_action_set_sensitive( set_action, FALSE );
5492         }
5493     }
5495     spinbutton_defocus(GTK_OBJECT(tbl));
5498 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5499     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5500     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5501     spinbutton_defocus(GTK_OBJECT(tbl));
5505 /**
5506  * Dropper auxiliary toolbar construction and setup.
5507  *
5508  * TODO: Would like to add swatch of current color.
5509  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5510  *       can drag and drop places. Will provide a nice mixing palette.
5511  */
5512 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5514     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5515     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5517     {
5518         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5519         ege_output_action_set_use_markup( act, TRUE );
5520         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5521     }
5523     {
5524         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5525                                                       _("Pick opacity"),
5526                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5527                                                       NULL,
5528                                                       Inkscape::ICON_SIZE_DECORATION );
5529         g_object_set( act, "short_label", _("Pick"), NULL );
5530         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5531         g_object_set_data( holder, "pick_action", act );
5532         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5533         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5534     }
5536     {
5537         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5538                                                       _("Assign opacity"),
5539                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5540                                                       NULL,
5541                                                       Inkscape::ICON_SIZE_DECORATION );
5542         g_object_set( act, "short_label", _("Assign"), NULL );
5543         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5544         g_object_set_data( holder, "set_action", act );
5545         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5546         // make sure it's disabled if we're not picking alpha
5547         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5548         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5549     }
5553 //########################
5554 //##      LPETool       ##
5555 //########################
5557 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5559 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5560 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5562     using namespace Inkscape::LivePathEffect;
5564     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5565     SPEventContext *ec = desktop->event_context;
5566     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5567         return;
5568     }
5570     // only take action if run by the attr_changed listener
5571     if (!g_object_get_data(tbl, "freeze")) {
5572         // in turn, prevent listener from responding
5573         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5575         gint mode = ege_select_one_action_get_active(act);
5576         EffectType type = lpesubtools[mode].type;
5578         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5579         bool success = lpetool_try_construction(lc, type);
5580         if (success) {
5581             // since the construction was already performed, we set the state back to inactive
5582             ege_select_one_action_set_active(act, 0);
5583             mode = 0;
5584         } else {
5585             // switch to the chosen subtool
5586             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5587         }
5589         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5590             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5591             prefs->setInt( "/tools/lpetool/mode", mode );
5592         }
5594         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5595     }
5598 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5600     SPEventContext *ec = selection->desktop()->event_context;
5601     if (!SP_IS_LPETOOL_CONTEXT(ec))
5602         return;
5604     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5607 void
5608 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5610     using namespace Inkscape::LivePathEffect;
5611     SPEventContext *ec = selection->desktop()->event_context;
5612     if (!SP_IS_LPETOOL_CONTEXT(ec))
5613         return;
5614     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5616     lpetool_delete_measuring_items(lc);
5617     lpetool_create_measuring_items(lc, selection);
5619     // activate line segment combo box if a single item with LPELineSegment is selected
5620     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5621     SPItem *item = selection->singleItem();
5622     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5623         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5624         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5625         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5626             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5627             g_object_set_data(tbl, "currentlpe", lpe);
5628             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5629             gtk_action_set_sensitive(w, TRUE);
5630             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5631         } else {
5632             g_object_set_data(tbl, "currentlpe", NULL);
5633             g_object_set_data(tbl, "currentlpeitem", NULL);
5634             gtk_action_set_sensitive(w, FALSE);
5635         }
5636     } else {
5637         g_object_set_data(tbl, "currentlpe", NULL);
5638         g_object_set_data(tbl, "currentlpeitem", NULL);
5639         gtk_action_set_sensitive(w, FALSE);
5640     }
5643 static void
5644 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5645     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5646     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5648     bool show = gtk_toggle_action_get_active( act );
5649     prefs->setBool("/tools/lpetool/show_bbox",  show);
5651     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5652         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5653         lpetool_context_reset_limiting_bbox(lc);
5654     }
5657 static void
5658 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5659     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5660     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5661         return;
5663     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5664     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5665     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5666         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5667         bool show = gtk_toggle_action_get_active( act );
5668         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5669         lpetool_show_measuring_info(lc, show);
5670         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5671     }
5674 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5675     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5676     SPUnit const *unit = tracker->getActiveUnit();
5677     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5678     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5680     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5681     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5682         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5683         lpetool_delete_measuring_items(lc);
5684         lpetool_create_measuring_items(lc);
5685     }
5688 static void
5689 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5690     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5691     Inkscape::Selection *selection = desktop->selection;
5693     Geom::OptRect bbox = selection->bounds();
5695     if (bbox) {
5696         Geom::Point A(bbox->min());
5697         Geom::Point B(bbox->max());
5699         A *= desktop->doc2dt();
5700         B *= desktop->doc2dt();
5702         // TODO: should we provide a way to store points in prefs?
5703         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5704         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5705         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5706         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5707         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5709         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5710     }
5712     gtk_toggle_action_set_active(act, false);
5715 static void
5716 sp_line_segment_build_list(GObject *tbl)
5718     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5720     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5721     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5722     gtk_list_store_clear (model);
5724     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5725     {
5726         GtkTreeIter iter;
5727         gtk_list_store_append( model, &iter );
5728         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5729         gtk_list_store_append( model, &iter );
5730         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5731         gtk_list_store_append( model, &iter );
5732         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5733         gtk_list_store_append( model, &iter );
5734         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5735     }
5737     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5740 static void
5741 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5742     using namespace Inkscape::LivePathEffect;
5744     // quit if run by the attr_changed listener
5745     if (g_object_get_data(tbl, "freeze")) {
5746         return;
5747     }
5749     // in turn, prevent listener from responding
5750     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5752     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5753     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5754     if (lpeitem) {
5755         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5756         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5757         sp_lpe_item_update_patheffect(lpeitem, true, true);
5758     }
5760     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5763 static void
5764 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5765     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5767     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5768         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5769     }
5770     gtk_toggle_action_set_active(act, false);
5773 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5775     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5776     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5777     g_object_set_data(holder, "tracker", tracker);
5778     SPUnit const *unit = tracker->getActiveUnit();
5780     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5781     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5783     /** Automatically create a list of LPEs that get added to the toolbar **/
5784     {
5785         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5787         GtkTreeIter iter;
5789         // the first toggle button represents the state that no subtool is active (remove this when
5790         // this can be modeled by EgeSelectOneAction or some other action)
5791         gtk_list_store_append( model, &iter );
5792         gtk_list_store_set( model, &iter,
5793                             0, _("All inactive"),
5794                             1, _("No geometric tool is active"),
5795                             2, "draw-geometry-inactive",
5796                             -1 );
5798         Inkscape::LivePathEffect::EffectType type;
5799         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5800             type =  lpesubtools[i].type;
5801             gtk_list_store_append( model, &iter );
5802             gtk_list_store_set( model, &iter,
5803                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5804                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5805                                 2, lpesubtools[i].icon_name,
5806                                 -1 );
5807         }
5809         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5810         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5811         g_object_set_data( holder, "lpetool_mode_action", act );
5813         ege_select_one_action_set_appearance( act, "full" );
5814         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5815         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5816         ege_select_one_action_set_icon_column( act, 2 );
5817         ege_select_one_action_set_tooltip_column( act, 1  );
5819         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5820         ege_select_one_action_set_active( act, lpeToolMode );
5821         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5822     }
5824     /* Show limiting bounding box */
5825     {
5826         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5827                                                       _("Show limiting bounding box"),
5828                                                       _("Show bounding box (used to cut infinite lines)"),
5829                                                       "show-bounding-box",
5830                                                       Inkscape::ICON_SIZE_DECORATION );
5831         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5832         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5833         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5834     }
5836     /* Set limiting bounding box to bbox of current selection */
5837     {
5838         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5839                                                       _("Get limiting bounding box from selection"),
5840                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5841                                                       "draw-geometry-set-bounding-box",
5842                                                       Inkscape::ICON_SIZE_DECORATION );
5843         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5844         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5845         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5846     }
5849     /* Combo box to choose line segment type */
5850     {
5851         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5852         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5853         ege_select_one_action_set_appearance (act, "compact");
5854         g_object_set_data (holder, "lpetool_line_segment_action", act );
5856         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5858         sp_line_segment_build_list (holder);
5860         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5861         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5862         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5863     }
5865     /* Display measuring info for selected items */
5866     {
5867         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5868                                                       _("Display measuring info"),
5869                                                       _("Display measuring info for selected items"),
5870                                                       "draw-geometry-show-measuring-info",
5871                                                       Inkscape::ICON_SIZE_DECORATION );
5872         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5873         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5874         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5875     }
5877     // add the units menu
5878     {
5879         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5880         gtk_action_group_add_action( mainActions, act );
5881         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5882         g_object_set_data(holder, "lpetool_units_action", act);
5883         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5884     }
5886     /* Open LPE dialog (to adapt parameters numerically) */
5887     {
5888         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5889                                                       _("Open LPE dialog"),
5890                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5891                                                       "dialog-geometry",
5892                                                       Inkscape::ICON_SIZE_DECORATION );
5893         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5894         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5895         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5896     }
5898     //watch selection
5899     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5901     sigc::connection *c_selection_modified =
5902         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5903                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5904     pool->add_connection ("selection-modified", c_selection_modified);
5906     sigc::connection *c_selection_changed =
5907         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5908                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5909     pool->add_connection ("selection-changed", c_selection_changed);
5912 //########################
5913 //##       Eraser       ##
5914 //########################
5916 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5918     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5919     prefs->setDouble( "/tools/eraser/width", adj->value );
5920     update_presets_list(tbl);
5923 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5925     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5926     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5927     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5928         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5929         prefs->setBool( "/tools/eraser/mode", eraserMode );
5930     }
5932     // only take action if run by the attr_changed listener
5933     if (!g_object_get_data( tbl, "freeze" )) {
5934         // in turn, prevent listener from responding
5935         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5937         if ( eraserMode != 0 ) {
5938         } else {
5939         }
5940         // TODO finish implementation
5942         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5943     }
5946 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5948     {
5949         /* Width */
5950         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5951         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5952         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5953                                                               _("Pen Width"), _("Width:"),
5954                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5955                                                               "/tools/eraser/width", 15,
5956                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5957                                                               1, 100, 1.0, 10.0,
5958                                                               labels, values, G_N_ELEMENTS(labels),
5959                                                               sp_erc_width_value_changed, 1, 0);
5960         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5961         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5962         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5963     }
5965     {
5966         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5968         GtkTreeIter iter;
5969         gtk_list_store_append( model, &iter );
5970         gtk_list_store_set( model, &iter,
5971                             0, _("Delete"),
5972                             1, _("Delete objects touched by the eraser"),
5973                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5974                             -1 );
5976         gtk_list_store_append( model, &iter );
5977         gtk_list_store_set( model, &iter,
5978                             0, _("Cut"),
5979                             1, _("Cut out from objects"),
5980                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5981                             -1 );
5983         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5984         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5985         g_object_set_data( holder, "eraser_mode_action", act );
5987         ege_select_one_action_set_appearance( act, "full" );
5988         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5989         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5990         ege_select_one_action_set_icon_column( act, 2 );
5991         ege_select_one_action_set_tooltip_column( act, 1  );
5993         /// @todo Convert to boolean?
5994         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5995         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5996         ege_select_one_action_set_active( act, eraserMode );
5997         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5998     }
6002 //########################
6003 //##    Text Toolbox    ##
6004 //########################
6005 /*
6006 static void
6007 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6009     //Call back for letter sizing spinbutton
6012 static void
6013 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6015     //Call back for line height spinbutton
6018 static void
6019 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6021     //Call back for horizontal kerning spinbutton
6024 static void
6025 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6027     //Call back for vertical kerning spinbutton
6030 static void
6031 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6033     //Call back for letter rotation spinbutton
6034 }*/
6036 namespace {
6038 void
6039 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6041     // quit if run by the _changed callbacks
6042     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6043         return;
6044     }
6046     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6048     SPStyle *query =
6049         sp_style_new (SP_ACTIVE_DOCUMENT);
6051     int result_family =
6052         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6054     int result_style =
6055         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6057     int result_numbers =
6058         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6060     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6062     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6063     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6064         // there are no texts in selection, read from prefs
6066             sp_style_read_from_prefs(query, "/tools/text");
6068             if (g_object_get_data(tbl, "text_style_from_prefs")) {
6069             // do not reset the toolbar style from prefs if we already did it last time
6070             sp_style_unref(query);
6071             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6072             return;
6073         }
6074         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6075     } else {
6076         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6077     }
6079     if (query->text)
6080     {
6081         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6082             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6083             gtk_entry_set_text (GTK_ENTRY (entry), "");
6085         } else if (query->text->font_specification.value || query->text->font_family.value) {
6087             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6088             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6090             // Get the font that corresponds
6091             Glib::ustring familyName;
6093             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6094             if (font) {
6095                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6096                 font->Unref();
6097                 font = NULL;
6098             }
6100             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6102             Gtk::TreeIter iter;
6103             try {
6104                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6105                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6106                 iter = model->get_iter(path);
6107             } catch (...) {
6108                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6109                 sp_style_unref(query);
6110                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6111                 return;
6112             }
6114             combo->set_active (iter);
6115         }
6117         //Size
6118         {
6119             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6120             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6121             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6122             g_free(str);
6123         }
6125         //Anchor
6126         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6127         {
6128             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6129             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6130             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6131             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6132         }
6133         else
6134         {
6135             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6136             {
6137                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6138                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6139                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6140                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6141             }
6142             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6143             {
6144                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6145                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6146                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6147                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6148             }
6149             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6150             {
6151                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6152                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6153                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6154                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6155             }
6156         }
6158         //Style
6159         {
6160             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6162             gboolean active = gtk_toggle_button_get_active (button);
6163             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));
6165             if (active != check)
6166             {
6167                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6168                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6169                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6170             }
6171         }
6173         {
6174             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6176             gboolean active = gtk_toggle_button_get_active (button);
6177             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6179             if (active != check)
6180             {
6181                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6182                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6183                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6184             }
6185         }
6187         //Orientation
6188         //locking both buttons, changing one affect all group (both)
6189         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6190         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6192         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6193         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6195         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6196         {
6197             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6198         }
6199         else
6200         {
6201             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6202         }
6203         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6204         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6205     }
6207     sp_style_unref(query);
6209     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6212 void
6213 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6215     sp_text_toolbox_selection_changed (selection, tbl);
6218 void
6219 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6221     sp_text_toolbox_selection_changed (NULL, tbl);
6224 void
6225 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6226                                 GObject             *tbl)
6228     // quit if run by the _changed callbacks
6229     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6230         return;
6231     }
6233     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6235     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6236     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6237     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6239     //g_print ("family changed to: %s\n", family);
6241     SPStyle *query =
6242         sp_style_new (SP_ACTIVE_DOCUMENT);
6244     int result_fontspec =
6245         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6247     SPCSSAttr *css = sp_repr_css_attr_new ();
6249     // First try to get the font spec from the stored value
6250     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6252     if (fontSpec.empty()) {
6253         // Construct a new font specification if it does not yet exist
6254         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6255         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6256         fontFromStyle->Unref();
6257     }
6259     if (!fontSpec.empty()) {
6261         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6263         if (!newFontSpec.empty()) {
6265             if (fontSpec != newFontSpec) {
6267                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6269                 if (font) {
6270                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6272                     // Set all the these just in case they were altered when finding the best
6273                     // match for the new family and old style...
6275                     gchar c[256];
6277                     font->Family(c, 256);
6279                     sp_repr_css_set_property (css, "font-family", c);
6281                     font->Attribute( "weight", c, 256);
6282                     sp_repr_css_set_property (css, "font-weight", c);
6284                     font->Attribute("style", c, 256);
6285                     sp_repr_css_set_property (css, "font-style", c);
6287                     font->Attribute("stretch", c, 256);
6288                     sp_repr_css_set_property (css, "font-stretch", c);
6290                     font->Attribute("variant", c, 256);
6291                     sp_repr_css_set_property (css, "font-variant", c);
6293                     font->Unref();
6294                 }
6295             }
6297         } else {
6298             // If the old font on selection (or default) was not existing on the system,
6299             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6300             // setting the family reported by the family chooser.
6302             //g_print ("fallback setting family: %s\n", family);
6303             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6304             sp_repr_css_set_property (css, "font-family", family);
6305         }
6306     }
6308     // If querying returned nothing, set the default style of the tool (for new texts)
6309     if (result_fontspec == QUERY_STYLE_NOTHING)
6310     {
6311         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6312         prefs->mergeStyle("/tools/text/style", css);
6313         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6314     }
6315     else
6316     {
6317         sp_desktop_set_style (desktop, css, true, true);
6318     }
6320     sp_style_unref(query);
6322     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6323                                    _("Text: Change font family"));
6324     sp_repr_css_attr_unref (css);
6326     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6328     // unfreeze
6329     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6331     // focus to canvas
6332     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6336 void
6337 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6338                                    gpointer          data)
6340     if (g_object_get_data (G_OBJECT (button), "block")) return;
6341     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6342     int prop = GPOINTER_TO_INT(data);
6344     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6345     SPCSSAttr *css = sp_repr_css_attr_new ();
6347     switch (prop)
6348     {
6349         case 0:
6350         {
6351             sp_repr_css_set_property (css, "text-anchor", "start");
6352             sp_repr_css_set_property (css, "text-align", "start");
6353             break;
6354         }
6355         case 1:
6356         {
6357             sp_repr_css_set_property (css, "text-anchor", "middle");
6358             sp_repr_css_set_property (css, "text-align", "center");
6359             break;
6360         }
6362         case 2:
6363         {
6364             sp_repr_css_set_property (css, "text-anchor", "end");
6365             sp_repr_css_set_property (css, "text-align", "end");
6366             break;
6367         }
6369         case 3:
6370         {
6371             sp_repr_css_set_property (css, "text-anchor", "start");
6372             sp_repr_css_set_property (css, "text-align", "justify");
6373             break;
6374         }
6375     }
6377     SPStyle *query =
6378         sp_style_new (SP_ACTIVE_DOCUMENT);
6379     int result_numbers =
6380         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6382     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6383     if (result_numbers == QUERY_STYLE_NOTHING)
6384     {
6385         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6386         prefs->mergeStyle("/tools/text/style", css);
6387     }
6389     sp_style_unref(query);
6391     sp_desktop_set_style (desktop, css, true, true);
6392     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6393                                    _("Text: Change alignment"));
6394     sp_repr_css_attr_unref (css);
6396     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6399 void
6400 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6401                                gpointer          data)
6403     if (g_object_get_data (G_OBJECT (button), "block")) return;
6405     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6406     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6407     int          prop       = GPOINTER_TO_INT(data);
6408     bool         active     = gtk_toggle_button_get_active (button);
6410     SPStyle *query =
6411         sp_style_new (SP_ACTIVE_DOCUMENT);
6413     int result_fontspec =
6414         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6416     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6417     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6418     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6420     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6421     Glib::ustring newFontSpec = "";
6423     if (fontSpec.empty()) {
6424         // Construct a new font specification if it does not yet exist
6425         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6426         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6427         fontFromStyle->Unref();
6428     }
6430     bool nochange = true;
6431     switch (prop)
6432     {
6433         case 0:
6434         {
6435             if (!fontSpec.empty()) {
6436                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6437                 if (!newFontSpec.empty()) {
6438                     // Don't even set the bold if the font didn't exist on the system
6439                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6440                     nochange = false;
6441                 }
6442             }
6443             // set or reset the button according
6444             if(nochange) {
6445                 gboolean check = gtk_toggle_button_get_active (button);
6447                 if (active != check)
6448                 {
6449                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6450                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6451                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6452                 }
6453             }
6455             break;
6456         }
6458         case 1:
6459         {
6460             if (!fontSpec.empty()) {
6461                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6462                 if (!newFontSpec.empty()) {
6463                     // Don't even set the italic if the font didn't exist on the system
6464                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6465                     nochange = false;
6466                 }
6467             }
6468             if(nochange) {
6469                 gboolean check = gtk_toggle_button_get_active (button);
6471                 if (active != check)
6472                 {
6473                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6474                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6475                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6476                 }
6477             }
6478             break;
6479         }
6480     }
6482     if (!newFontSpec.empty()) {
6483         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6484     }
6486     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6487     if (result_fontspec == QUERY_STYLE_NOTHING)
6488     {
6489         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6490         prefs->mergeStyle("/tools/text/style", css);
6491     }
6493     sp_style_unref(query);
6495     sp_desktop_set_style (desktop, css, true, true);
6496     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6497                                    _("Text: Change font style"));
6498     sp_repr_css_attr_unref (css);
6500     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6503 void
6504 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6505                                      gpointer         data)
6507     if (g_object_get_data (G_OBJECT (button), "block")) {
6508         return;
6509     }
6511     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6512     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6513     int          prop       = GPOINTER_TO_INT(data);
6515     switch (prop)
6516     {
6517         case 0:
6518         {
6519             sp_repr_css_set_property (css, "writing-mode", "lr");
6520             break;
6521         }
6523         case 1:
6524         {
6525             sp_repr_css_set_property (css, "writing-mode", "tb");
6526             break;
6527         }
6528     }
6530     SPStyle *query =
6531         sp_style_new (SP_ACTIVE_DOCUMENT);
6532     int result_numbers =
6533         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6535     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6536     if (result_numbers == QUERY_STYLE_NOTHING)
6537     {
6538         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6539         prefs->mergeStyle("/tools/text/style", css);
6540     }
6542     sp_desktop_set_style (desktop, css, true, true);
6543     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6544                                    _("Text: Change orientation"));
6545     sp_repr_css_attr_unref (css);
6547     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6550 gboolean
6551 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6553     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6554     if (!desktop) return FALSE;
6556     switch (get_group0_keyval (event)) {
6557         case GDK_KP_Enter: // chosen
6558         case GDK_Return:
6559             // unfreeze and update, which will defocus
6560             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6561             sp_text_toolbox_family_changed (NULL, tbl);
6562             return TRUE; // I consumed the event
6563             break;
6564         case GDK_Escape:
6565             // defocus
6566             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6567             return TRUE; // I consumed the event
6568             break;
6569     }
6570     return FALSE;
6573 gboolean
6574 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6576     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6577     if (!desktop) return FALSE;
6579     switch (get_group0_keyval (event)) {
6580         case GDK_KP_Enter:
6581         case GDK_Return:
6582         case GDK_Escape: // defocus
6583             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6584             return TRUE; // I consumed the event
6585             break;
6586         case GDK_w:
6587         case GDK_W:
6588             if (event->state & GDK_CONTROL_MASK) {
6589                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6590                 return TRUE; // I consumed the event
6591             }
6592             break;
6593     }
6594     return FALSE;
6598 void
6599 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6600                                GObject     *tbl)
6602      // quit if run by the _changed callbacks
6603     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6604         return;
6605     }
6607     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6609    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6611     // If this is not from selecting a size in the list (in which case get_active will give the
6612     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6613     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6614     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6615    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6616         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6617         return;
6618    }
6620     gdouble value = -1;
6621     {
6622         gchar *endptr;
6623         gchar *const text = gtk_combo_box_get_active_text(cbox);
6624         if (text) {
6625             value = g_strtod(text, &endptr);
6626             if (endptr == text) {  // Conversion failed, non-numeric input.
6627                 value = -1;
6628             }
6629             g_free(text);
6630         }
6631     }
6632     if (value <= 0) {
6633         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6634         return; // could not parse value
6635     }
6637     SPCSSAttr *css = sp_repr_css_attr_new ();
6638     Inkscape::CSSOStringStream osfs;
6639     osfs << value;
6640     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6642     SPStyle *query =
6643         sp_style_new (SP_ACTIVE_DOCUMENT);
6644     int result_numbers =
6645         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6647     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6648     if (result_numbers == QUERY_STYLE_NOTHING)
6649     {
6650         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6651         prefs->mergeStyle("/tools/text/style", css);
6652     }
6654     sp_style_unref(query);
6656     sp_desktop_set_style (desktop, css, true, true);
6657     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6658                                    _("Text: Change font size"));
6659     sp_repr_css_attr_unref (css);
6661     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6663     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6666 gboolean
6667 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6669     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6670     if (!desktop) return FALSE;
6672     if (!g_object_get_data (tbl, "esc-pressed")) {
6673         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6674         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6675         sp_text_toolbox_size_changed (cbox, tbl);
6676         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6677     }
6678     return FALSE; // I consumed the event
6682 gboolean
6683 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6685     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6686     if (!desktop) return FALSE;
6688     switch (get_group0_keyval (event)) {
6689         case GDK_Escape: // defocus
6690             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6691             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6692             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6693             return TRUE; // I consumed the event
6694             break;
6695         case GDK_Return: // defocus
6696         case GDK_KP_Enter:
6697             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6698             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6699             sp_text_toolbox_size_changed (cbox, tbl);
6700             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6701             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6702             return TRUE; // I consumed the event
6703             break;
6704     }
6705     return FALSE;
6708 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6709 // does not work!
6710 gboolean
6711 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6712                                  GdkEventFocus    */*event*/,
6713                                  GObject          *tbl)
6715     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6716     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6717     return FALSE;
6720 gboolean
6721 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6722                                  GdkEventFocus    */*event*/,
6723                                  GObject          *tbl)
6725     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6726     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6727     return FALSE;
6730 void
6731 cell_data_func  (GtkCellLayout */*cell_layout*/,
6732                  GtkCellRenderer   *cell,
6733                  GtkTreeModel      *tree_model,
6734                  GtkTreeIter       *iter,
6735                  gpointer           /*data*/)
6737     gchar *family;
6738     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6739     gchar *const family_escaped = g_markup_escape_text(family, -1);
6741     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6742     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6743     if (show_sample) {
6745         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6746         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6748     std::stringstream markup;
6749     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6750            << family_escaped << "'>" << sample_escaped << "</span>";
6751     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6753         g_free(sample_escaped);
6754     } else {
6755         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6756     }
6758     g_free(family);
6759     g_free(family_escaped);
6762 gboolean            text_toolbox_completion_match_selected    (GtkEntryCompletion *widget,
6763                                                         GtkTreeModel       *model,
6764                                                         GtkTreeIter        *iter,
6765                                                         GObject *tbl)
6767     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6768     // press Enter again after choosing a completion)
6769     gchar *family;
6770     gtk_tree_model_get(model, iter, 0, &family, -1);
6772     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6773     gtk_entry_set_text (GTK_ENTRY (entry), family);
6775     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6776     sp_text_toolbox_family_changed (NULL, tbl);
6777     return TRUE;
6781 static void
6782 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6783     GtkEntry *entry;
6784     GtkEntryCompletion *completion;
6785     GtkTreeModel *model;
6787     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6788     completion = gtk_entry_completion_new();
6789     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6790     gtk_entry_completion_set_model(completion, model);
6791     gtk_entry_completion_set_text_column(completion, 0);
6792     gtk_entry_completion_set_inline_completion(completion, FALSE);
6793     gtk_entry_completion_set_inline_selection(completion, FALSE);
6794     gtk_entry_completion_set_popup_completion(completion, TRUE);
6795     gtk_entry_set_completion(entry, completion);
6797     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6799     g_object_unref(completion);
6802 void        sp_text_toolbox_family_popnotify          (GtkComboBox *widget,
6803                                                        void *property,
6804                                                         GObject *tbl)
6806   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6808   gboolean shown;
6809   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6810   if (shown) {
6811          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6812          //g_print("POP: notify: SHOWN\n");
6813   } else {
6814          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6816          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6817          // find out if the drop down was closed by Enter and if so, manually update (only
6818          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6819          GdkEvent *ev = gtk_get_current_event();
6820          if (ev) {
6821              //g_print ("ev type: %d\n", ev->type);
6822              if (ev->type == GDK_KEY_PRESS) {
6823                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6824                      case GDK_KP_Enter: // chosen
6825                      case GDK_Return:
6826                      {
6827                          // make sure the chosen one is inserted into the entry
6828                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6829                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6830                          GtkTreeIter iter;
6831                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6832                          if (has_active) {
6833                              gchar *family;
6834                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6835                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6836                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6837                          }
6839                          // update
6840                          sp_text_toolbox_family_changed (NULL, tbl);
6841                          break;
6842                      }
6843                  }
6844              }
6845          }
6847          // regardless of whether we updated, defocus the widget
6848          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6849          if (desktop)
6850              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6851          //g_print("POP: notify: HIDDEN\n");
6852   }
6855 GtkWidget*
6856 sp_text_toolbox_new (SPDesktop *desktop)
6858     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6859     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6861     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6862     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6864     GtkTooltips *tt = gtk_tooltips_new();
6866     ////////////Family
6867     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6868     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6870     gtk_rc_parse_string (
6871        "style \"dropdown-as-list-style\"\n"
6872        "{\n"
6873        "    GtkComboBox::appears-as-list = 1\n"
6874        "}\n"
6875        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6876     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6877     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6879     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6881     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6883     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6884     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6886     // expand the field a bit so as to view more of the previews in the drop-down
6887     GtkRequisition req;
6888     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6889     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6891     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6892     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6894     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6895     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6896              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6897     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6898     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6899     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6901     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6902     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6904     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6905     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6906     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6907     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6909     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6910     GtkWidget *box = gtk_event_box_new ();
6911     gtk_container_add (GTK_CONTAINER (box), image);
6912     gtk_toolbar_append_widget( tbl, box, "", "");
6913     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6914     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6915     gtk_widget_hide (GTK_WIDGET (box));
6916     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6918     ////////////Size
6919     gchar const *const sizes[] = {
6920         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6921         "16", "18", "20", "22", "24", "28",
6922         "32", "36", "40", "48", "56", "64", "72", "144"
6923     };
6925     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6926     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6927         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6928     }
6929     gtk_widget_set_size_request (cbox, 80, -1);
6930     gtk_toolbar_append_widget( tbl, cbox, "", "");
6931     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6932     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6933     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6934     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6936     ////////////Text anchor
6937     GtkWidget *group   = gtk_radio_button_new (NULL);
6938     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6939     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6941     // left
6942     GtkWidget *rbutton = group;
6943     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6944     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6945     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6947     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6948     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6949     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6950     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6952     // center
6953     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6954     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6955     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6956     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6958     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6959     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6960     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6961     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6963     // right
6964     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6965     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6966     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6967     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6969     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6970     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6971     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6972     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6974     // fill
6975     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6976     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6977     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6978     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6980     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6981     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6982     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6983     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6985     gtk_toolbar_append_widget( tbl, row, "", "");
6987     //spacer
6988     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6990     ////////////Text style
6991     row = gtk_hbox_new (FALSE, 4);
6993     // bold
6994     rbutton = gtk_toggle_button_new ();
6995     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6996     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6997     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6998     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7000     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7001     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7002     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7004     // italic
7005     rbutton = gtk_toggle_button_new ();
7006     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7007     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7008     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7009     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7011     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7012     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7013     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7015     gtk_toolbar_append_widget( tbl, row, "", "");
7017     //spacer
7018     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7020     // Text orientation
7021     group   = gtk_radio_button_new (NULL);
7022     row     = gtk_hbox_new (FALSE, 4);
7023     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7025     // horizontal
7026     rbutton = group;
7027     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7028     gtk_container_add           (GTK_CONTAINER (rbutton),
7029                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7030     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7031     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7033     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7034     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7035     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7037     // vertical
7038     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7039     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7040     gtk_container_add           (GTK_CONTAINER (rbutton),
7041                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7042     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7043     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7045     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7046     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7047     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7048     gtk_toolbar_append_widget( tbl, row, "", "" );
7051     //watch selection
7052     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7054     sigc::connection *c_selection_changed =
7055         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7056                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7057     pool->add_connection ("selection-changed", c_selection_changed);
7059     sigc::connection *c_selection_modified =
7060         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7061                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7062     pool->add_connection ("selection-modified", c_selection_modified);
7064     sigc::connection *c_subselection_changed =
7065         new sigc::connection (desktop->connectToolSubselectionChanged
7066                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7067     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7069     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7072     gtk_widget_show_all( GTK_WIDGET(tbl) );
7074     return GTK_WIDGET(tbl);
7075 } // end of sp_text_toolbox_new()
7077 }//<unnamed> namespace
7080 //#########################
7081 //##      Connector      ##
7082 //#########################
7084 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7086     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7087     prefs->setBool("/tools/connector/mode",
7088                 gtk_toggle_action_get_active( act ));
7091 static void sp_connector_path_set_avoid(void)
7093     cc_selection_set_avoid(true);
7097 static void sp_connector_path_set_ignore(void)
7099     cc_selection_set_avoid(false);
7102 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7104     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7105     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7106     SPDocument *doc = sp_desktop_document(desktop);
7108     if (!sp_document_get_undo_sensitive(doc))
7109     {
7110         return;
7111     }
7112     
7114     // quit if run by the _changed callbacks
7115     if (g_object_get_data( tbl, "freeze" )) {
7116         return;
7117     }
7119     // in turn, prevent callbacks from responding
7120     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7122     bool is_orthog = gtk_toggle_action_get_active( act );
7123     gchar orthog_str[] = "orthogonal";
7124     gchar polyline_str[] = "polyline";
7125     gchar *value = is_orthog ? orthog_str : polyline_str ;
7127     bool modmade = false;
7128     GSList *l = (GSList *) selection->itemList();
7129     while (l) {
7130         SPItem *item = (SPItem *) l->data;
7132         if (cc_item_is_connector(item)) {
7133             sp_object_setAttribute(item, "inkscape:connector-type",
7134                     value, false);
7135             item->avoidRef->handleSettingChange();
7136             modmade = true;
7137         }
7138         l = l->next;
7139     }
7141     if (!modmade)
7142     {
7143         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7144         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7145     }
7147     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7148             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7150     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7153 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7155     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7156     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7157     SPDocument *doc = sp_desktop_document(desktop);
7159     if (!sp_document_get_undo_sensitive(doc))
7160     {
7161         return;
7162     }
7163     
7165     // quit if run by the _changed callbacks
7166     if (g_object_get_data( tbl, "freeze" )) {
7167         return;
7168     }
7170     // in turn, prevent callbacks from responding
7171     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7173     gdouble newValue = gtk_adjustment_get_value(adj);
7174     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7175     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7177     bool modmade = false;
7178     GSList *l = (GSList *) selection->itemList();
7179     while (l) {
7180         SPItem *item = (SPItem *) l->data;
7182         if (cc_item_is_connector(item)) {
7183             sp_object_setAttribute(item, "inkscape:connector-curvature",
7184                     value, false);
7185             item->avoidRef->handleSettingChange();
7186             modmade = true;
7187         }
7188         l = l->next;
7189     }
7191     if (!modmade)
7192     {
7193         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7194         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7195     }
7197     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7198             _("Change connector curvature"));
7200     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7204 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7206     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7207     SPDocument *doc = sp_desktop_document(desktop);
7209     if (!sp_document_get_undo_sensitive(doc))
7210     {
7211         return;
7212     }
7214     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7216     if ( !repr->attribute("inkscape:connector-spacing") &&
7217             ( adj->value == defaultConnSpacing )) {
7218         // Don't need to update the repr if the attribute doesn't
7219         // exist and it is being set to the default value -- as will
7220         // happen at startup.
7221         return;
7222     }
7224     // quit if run by the attr_changed listener
7225     if (g_object_get_data( tbl, "freeze" )) {
7226         return;
7227     }
7229     // in turn, prevent listener from responding
7230     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7232     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7233     SP_OBJECT(desktop->namedview)->updateRepr();
7235     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7236     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7237         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7238         Geom::Matrix m = Geom::identity();
7239         avoid_item_move(&m, item);
7240     }
7242     if (items) {
7243         g_slist_free(items);
7244     }
7246     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7247             _("Change connector spacing"));
7249     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7252 static void sp_connector_graph_layout(void)
7254     if (!SP_ACTIVE_DESKTOP) return;
7255     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7257     // hack for clones, see comment in align-and-distribute.cpp
7258     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7259     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7261     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7263     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7265     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7268 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7270     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7271     prefs->setBool("/tools/connector/directedlayout",
7272                 gtk_toggle_action_get_active( act ));
7275 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7277     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7278     prefs->setBool("/tools/connector/avoidoverlaplayout",
7279                 gtk_toggle_action_get_active( act ));
7283 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
7285     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7286     prefs->setDouble("/tools/connector/length", adj->value);
7289 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7290                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7291                                             bool /*is_interactive*/, gpointer data)
7293     GtkWidget *tbl = GTK_WIDGET(data);
7295     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7296         return;
7297     }
7298     if (strcmp(name, "inkscape:connector-spacing") == 0)
7299     {
7300         GtkAdjustment *adj = (GtkAdjustment*)
7301                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7302         gdouble spacing = defaultConnSpacing;
7303         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7305         gtk_adjustment_set_value(adj, spacing);
7306         gtk_adjustment_value_changed(adj);
7307     }
7309     spinbutton_defocus(GTK_OBJECT(tbl));
7312 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7314     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7315     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7317     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7318         cc_create_connection_point(cc);
7321 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7323     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7324     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7326     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7327         cc_remove_connection_point(cc);
7330 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7331     NULL, /* child_added */
7332     NULL, /* child_removed */
7333     connector_tb_event_attr_changed,
7334     NULL, /* content_changed */
7335     NULL  /* order_changed */
7336 };
7338 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7340     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7341     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7342     SPItem *item = selection->singleItem();
7343     if (SP_IS_PATH(item))
7344     {
7345         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7346         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7347         gtk_toggle_action_set_active(act, is_orthog);
7348         gtk_adjustment_set_value(adj, curvature);
7349     }
7353 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7355     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7356     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7358     // Editing mode toggle button
7359     {
7360         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7361                                                       _("EditMode"),
7362                                                       _("Switch between connection point editing and connector drawing mode"),
7363                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7364                                                       Inkscape::ICON_SIZE_DECORATION );
7365         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7367         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7368         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7369         g_object_set_data( holder, "mode", act );
7370         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7371     }
7374     {
7375         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7376                                           _("Avoid"),
7377                                           _("Make connectors avoid selected objects"),
7378                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7379                                           secondarySize );
7380         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7381         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7382     }
7384     {
7385         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7386                                           _("Ignore"),
7387                                           _("Make connectors ignore selected objects"),
7388                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7389                                           secondarySize );
7390         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7391         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7392     }
7394     // Orthogonal connectors toggle button
7395     {
7396         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7397                                                       _("Orthogonal"),
7398                                                       _("Make connector orthogonal or polyline"),
7399                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7400                                                       Inkscape::ICON_SIZE_DECORATION );
7401         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7403         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7404         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7405         g_object_set_data( holder, "orthogonal", act );
7406         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7407     }
7409     EgeAdjustmentAction* eact = 0;
7410     // Curvature spinbox
7411     eact = create_adjustment_action( "ConnectorCurvatureAction",
7412                                     _("Connector Curvature"), _("Curvature:"),
7413                                     _("The amount of connectors curvature"),
7414                                     "/tools/connector/curvature", defaultConnCurvature,
7415                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7416                                     0, 100, 1.0, 10.0,
7417                                     0, 0, 0,
7418                                     connector_curvature_changed, 1, 0 );
7419     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7421     // Spacing spinbox
7422     eact = create_adjustment_action( "ConnectorSpacingAction",
7423                                     _("Connector Spacing"), _("Spacing:"),
7424                                     _("The amount of space left around objects by auto-routing connectors"),
7425                                     "/tools/connector/spacing", defaultConnSpacing,
7426                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7427                                     0, 100, 1.0, 10.0,
7428                                     0, 0, 0,
7429                                     connector_spacing_changed, 1, 0 );
7430     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7432     // Graph (connector network) layout
7433     {
7434         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7435                                           _("Graph"),
7436                                           _("Nicely arrange selected connector network"),
7437                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7438                                           secondarySize );
7439         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7440         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7441     }
7443     // Default connector length spinbox
7444     eact = create_adjustment_action( "ConnectorLengthAction",
7445                                      _("Connector Length"), _("Length:"),
7446                                      _("Ideal length for connectors when layout is applied"),
7447                                      "/tools/connector/length", 100,
7448                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7449                                      10, 1000, 10.0, 100.0,
7450                                      0, 0, 0,
7451                                      connector_length_changed, 1, 0 );
7452     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7455     // Directed edges toggle button
7456     {
7457         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7458                                                       _("Downwards"),
7459                                                       _("Make connectors with end-markers (arrows) point downwards"),
7460                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7461                                                       Inkscape::ICON_SIZE_DECORATION );
7462         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7464         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7465         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7467         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7468         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7469         );
7470     }
7472     // Avoid overlaps toggle button
7473     {
7474         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7475                                                       _("Remove overlaps"),
7476                                                       _("Do not allow overlapping shapes"),
7477                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7478                                                       Inkscape::ICON_SIZE_DECORATION );
7479         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7481         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7482         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7484         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7485     }
7488     // New connection point button
7489     {
7490         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7491                                           _("New connection point"),
7492                                           _("Add a new connection point to the currently selected item"),
7493                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7494                                           secondarySize );
7495         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7496         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7497     }
7498     
7499     // Remove selected connection point button
7500     
7501     {
7502         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7503                                           _("Remove connection point"),
7504                                           _("Remove the currently selected connection point"),
7505                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7506                                           secondarySize );
7507         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7508         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7509     }
7510     
7512     // Code to watch for changes to the connector-spacing attribute in
7513     // the XML.
7514     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7515     g_assert(repr != NULL);
7517     purge_repr_listener( holder, holder );
7519     if (repr) {
7520         g_object_set_data( holder, "repr", repr );
7521         Inkscape::GC::anchor(repr);
7522         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7523         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7524     }
7525 } // end of sp_connector_toolbox_prep()
7528 //#########################
7529 //##     Paintbucket     ##
7530 //#########################
7532 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7534     gint channels = ege_select_one_action_get_active( act );
7535     flood_channels_set_channels( channels );
7538 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7540     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7541     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7544 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7546     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7547     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7550 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7552     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7553     SPUnit const *unit = tracker->getActiveUnit();
7554     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7556     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7557     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7560 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7562     // FIXME: make defaults settable via Inkscape Options
7563     struct KeyValue {
7564         char const *key;
7565         double value;
7566     } const key_values[] = {
7567         {"threshold", 15},
7568         {"offset", 0.0}
7569     };
7571     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7572         KeyValue const &kv = key_values[i];
7573         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7574         if ( adj ) {
7575             gtk_adjustment_set_value(adj, kv.value);
7576         }
7577     }
7579     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7580     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7581     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7582     ege_select_one_action_set_active( autogap_action, 0 );
7585 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7587     EgeAdjustmentAction* eact = 0;
7588     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7590     {
7591         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7593         GList* items = 0;
7594         gint count = 0;
7595         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7596         {
7597             GtkTreeIter iter;
7598             gtk_list_store_append( model, &iter );
7599             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7600             count++;
7601         }
7602         g_list_free( items );
7603         items = 0;
7604         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7605         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7606         ege_select_one_action_set_appearance( act1, "compact" );
7607         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7608         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7609         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7610         g_object_set_data( holder, "channels_action", act1 );
7611     }
7613     // Spacing spinbox
7614     {
7615         eact = create_adjustment_action(
7616             "ThresholdAction",
7617             _("Fill Threshold"), _("Threshold:"),
7618             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7619             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7620             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7621             0, 0, 0,
7622             paintbucket_threshold_changed, 1, 0 );
7624         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7625         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7626     }
7628     // Create the units menu.
7629     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7630     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7631     if (!stored_unit.empty())
7632         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7633     g_object_set_data( holder, "tracker", tracker );
7634     {
7635         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7636         gtk_action_group_add_action( mainActions, act );
7637     }
7639     // Offset spinbox
7640     {
7641         eact = create_adjustment_action(
7642             "OffsetAction",
7643             _("Grow/shrink by"), _("Grow/shrink by:"),
7644             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7645             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7646             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7647             0, 0, 0,
7648             paintbucket_offset_changed, 1, 2);
7649         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7651         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7652     }
7654     /* Auto Gap */
7655     {
7656         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7658         GList* items = 0;
7659         gint count = 0;
7660         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7661         {
7662             GtkTreeIter iter;
7663             gtk_list_store_append( model, &iter );
7664             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7665             count++;
7666         }
7667         g_list_free( items );
7668         items = 0;
7669         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7670         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7671         ege_select_one_action_set_appearance( act2, "compact" );
7672         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7673         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7674         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7675         g_object_set_data( holder, "autogap_action", act2 );
7676     }
7678     /* Reset */
7679     {
7680         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7681                                           _("Defaults"),
7682                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7683                                           GTK_STOCK_CLEAR );
7684         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7685         gtk_action_group_add_action( mainActions, act );
7686         gtk_action_set_sensitive( act, TRUE );
7687     }
7691 /*
7692   Local Variables:
7693   mode:c++
7694   c-file-style:"stroustrup"
7695   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7696   indent-tabs-mode:nil
7697   fill-column:99
7698   End:
7699 */
7700 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :