Code

Refactoring copy-n-paste duplicate code out of toolbar creation.
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@users.sf.net>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *
16  * Copyright (C) 2004 David Turner
17  * Copyright (C) 2003 MenTaLguY
18  * Copyright (C) 1999-2008 authors
19  * Copyright (C) 2001-2002 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../node-context.h"
69 #include "../pen-context.h"
70 #include "../preferences.h"
71 #include "../selection-chemistry.h"
72 #include "../selection.h"
73 #include "select-toolbar.h"
74 #include "../shape-editor.h"
75 #include "../shortcuts.h"
76 #include "../sp-clippath.h"
77 #include "../sp-ellipse.h"
78 #include "../sp-flowtext.h"
79 #include "../sp-mask.h"
80 #include "../sp-namedview.h"
81 #include "../sp-rect.h"
82 #include "../sp-spiral.h"
83 #include "../sp-star.h"
84 #include "../sp-text.h"
85 #include "../style.h"
86 #include "../svg/css-ostringstream.h"
87 #include "../tools-switch.h"
88 #include "../tweak-context.h"
89 #include "../spray-context.h"
90 #include "../ui/dialog/calligraphic-profile-rename.h"
91 #include "../ui/icon-names.h"
92 #include "../ui/widget/style-swatch.h"
93 #include "../verbs.h"
94 #include "../widgets/button.h"
95 #include "../widgets/spinbutton-events.h"
96 #include "../widgets/spw-utilities.h"
97 #include "../widgets/widget-sizes.h"
98 #include "../xml/attribute-record.h"
99 #include "../xml/node-event-vector.h"
100 #include "../xml/repr.h"
102 #include "toolbox.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
123 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
131 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
132     static Inkscape::IconSize sizeChoices[] = {
133         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
134         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
135         Inkscape::ICON_SIZE_MENU
136     };
137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
138     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
139     return sizeChoices[index];
142 static struct {
143     gchar const *type_name;
144     gchar const *data_name;
145     sp_verb_t verb;
146     sp_verb_t doubleclick_verb;
147 } const tools[] = {
148     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
149     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
150     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
151     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
152     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
153     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
154     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
155     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
156     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
157     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
158     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
159     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
160     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
161     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
162     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
163     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
164     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
165     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
166     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
167     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
168     { NULL, NULL, 0, 0 }
169 };
171 static struct {
172     gchar const *type_name;
173     gchar const *data_name;
174     GtkWidget *(*create_func)(SPDesktop *desktop);
175     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
176     gchar const *ui_name;
177     gint swatch_verb_id;
178     gchar const *swatch_tool;
179     gchar const *swatch_tip;
180 } const aux_toolboxes[] = {
181     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
182       SP_VERB_INVALID, 0, 0},
183     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
184       SP_VERB_INVALID, 0, 0},
185     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
186       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
187     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
188       SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
189     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
190       SP_VERB_INVALID, 0, 0},
191     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
192       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
193     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
194       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
195     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
196       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
197     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
198       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
199     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
200       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
201     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
202       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
203     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
204       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
205     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
206       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
207     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
208       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
209     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
210       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
211     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
212       SP_VERB_INVALID, 0, 0},
213     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
214       SP_VERB_INVALID, 0, 0},
215     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
216       SP_VERB_INVALID, 0, 0},
217     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
218       SP_VERB_INVALID, 0, 0},
219     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
221     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227         "<ui>"
228         "  <toolbar name='SelectToolbar'>"
229         "    <toolitem action='EditSelectAll' />"
230         "    <toolitem action='EditSelectAllInAllLayers' />"
231         "    <toolitem action='EditDeselect' />"
232         "    <separator />"
233         "    <toolitem action='ObjectRotate90CCW' />"
234         "    <toolitem action='ObjectRotate90' />"
235         "    <toolitem action='ObjectFlipHorizontally' />"
236         "    <toolitem action='ObjectFlipVertically' />"
237         "    <separator />"
238         "    <toolitem action='SelectionToBack' />"
239         "    <toolitem action='SelectionLower' />"
240         "    <toolitem action='SelectionRaise' />"
241         "    <toolitem action='SelectionToFront' />"
242         "    <separator />"
243         "    <toolitem action='XAction' />"
244         "    <toolitem action='YAction' />"
245         "    <toolitem action='WidthAction' />"
246         "    <toolitem action='LockAction' />"
247         "    <toolitem action='HeightAction' />"
248         "    <toolitem action='UnitsAction' />"
249         "    <separator />"
250         "    <toolitem action='transform_affect_label' />"
251         "    <toolitem action='transform_stroke' />"
252         "    <toolitem action='transform_corners' />"
253         "    <toolitem action='transform_gradient' />"
254         "    <toolitem action='transform_pattern' />"
255         "  </toolbar>"
257         "  <toolbar name='NodeToolbar'>"
258         "    <toolitem action='NodeInsertAction' />"
259         "    <toolitem action='NodeDeleteAction' />"
260         "    <separator />"
261         "    <toolitem action='NodeJoinAction' />"
262         "    <toolitem action='NodeBreakAction' />"
263         "    <separator />"
264         "    <toolitem action='NodeJoinSegmentAction' />"
265         "    <toolitem action='NodeDeleteSegmentAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeCuspAction' />"
268         "    <toolitem action='NodeSmoothAction' />"
269         "    <toolitem action='NodeSymmetricAction' />"
270         "    <toolitem action='NodeAutoAction' />"
271         "    <separator />"
272         "    <toolitem action='NodeLineAction' />"
273         "    <toolitem action='NodeCurveAction' />"
274         "    <separator />"
275         "    <toolitem action='ObjectToPath' />"
276         "    <toolitem action='StrokeToPath' />"
277         "    <separator />"
278         "    <toolitem action='NodeXAction' />"
279         "    <toolitem action='NodeYAction' />"
280         "    <toolitem action='NodeUnitsAction' />"
281         "    <separator />"
282         "    <toolitem action='ObjectEditClipPathAction' />"
283         "    <toolitem action='ObjectEditMaskPathAction' />"
284         "    <toolitem action='EditNextLPEParameterAction' />"
285         "    <separator />"
286         "    <toolitem action='NodesShowHandlesAction' />"
287         "    <toolitem action='NodesShowHelperpath' />"
288         "  </toolbar>"
290         "  <toolbar name='TweakToolbar'>"
291         "    <toolitem action='TweakWidthAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakForceAction' />"
294         "    <toolitem action='TweakPressureAction' />"
295         "    <separator />"
296         "    <toolitem action='TweakModeAction' />"
297         "    <separator />"
298         "    <toolitem action='TweakFidelityAction' />"
299         "    <separator />"
300         "    <toolitem action='TweakChannelsLabel' />"
301         "    <toolitem action='TweakDoH' />"
302         "    <toolitem action='TweakDoS' />"
303         "    <toolitem action='TweakDoL' />"
304         "    <toolitem action='TweakDoO' />"
305         "  </toolbar>"
307         "  <toolbar name='SprayToolbar'>"
308         "    <toolitem action='SprayModeAction' />"
309         "    <separator />"
310         "    <separator />"
311         "    <toolitem action='SprayWidthAction' />"
312         "    <toolitem action='SprayPressureAction' />"
313         "    <toolitem action='SprayPopulationAction' />"
314         "    <separator />"
315         "    <toolitem action='SprayRotationAction' />"
316         "    <toolitem action='SprayScaleAction' />"
317         "    <separator />"
318         "    <toolitem action='SprayStandard_deviationAction' />"
319         "    <toolitem action='SprayMeanAction' />"
320         "  </toolbar>"
322         "  <toolbar name='ZoomToolbar'>"
323         "    <toolitem action='ZoomIn' />"
324         "    <toolitem action='ZoomOut' />"
325         "    <separator />"
326         "    <toolitem action='Zoom1:0' />"
327         "    <toolitem action='Zoom1:2' />"
328         "    <toolitem action='Zoom2:1' />"
329         "    <separator />"
330         "    <toolitem action='ZoomSelection' />"
331         "    <toolitem action='ZoomDrawing' />"
332         "    <toolitem action='ZoomPage' />"
333         "    <toolitem action='ZoomPageWidth' />"
334         "    <separator />"
335         "    <toolitem action='ZoomPrev' />"
336         "    <toolitem action='ZoomNext' />"
337         "  </toolbar>"
339         "  <toolbar name='StarToolbar'>"
340         "    <separator />"
341         "    <toolitem action='StarStateAction' />"
342         "    <separator />"
343         "    <toolitem action='FlatAction' />"
344         "    <separator />"
345         "    <toolitem action='MagnitudeAction' />"
346         "    <toolitem action='SpokeAction' />"
347         "    <toolitem action='RoundednessAction' />"
348         "    <toolitem action='RandomizationAction' />"
349         "    <separator />"
350         "    <toolitem action='StarResetAction' />"
351         "  </toolbar>"
353         "  <toolbar name='RectToolbar'>"
354         "    <toolitem action='RectStateAction' />"
355         "    <toolitem action='RectWidthAction' />"
356         "    <toolitem action='RectHeightAction' />"
357         "    <toolitem action='RadiusXAction' />"
358         "    <toolitem action='RadiusYAction' />"
359         "    <toolitem action='RectUnitsAction' />"
360         "    <separator />"
361         "    <toolitem action='RectResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='3DBoxToolbar'>"
365         "    <toolitem action='3DBoxAngleXAction' />"
366         "    <toolitem action='3DBoxVPXStateAction' />"
367         "    <separator />"
368         "    <toolitem action='3DBoxAngleYAction' />"
369         "    <toolitem action='3DBoxVPYStateAction' />"
370         "    <separator />"
371         "    <toolitem action='3DBoxAngleZAction' />"
372         "    <toolitem action='3DBoxVPZStateAction' />"
373         "  </toolbar>"
375         "  <toolbar name='SpiralToolbar'>"
376         "    <toolitem action='SpiralStateAction' />"
377         "    <toolitem action='SpiralRevolutionAction' />"
378         "    <toolitem action='SpiralExpansionAction' />"
379         "    <toolitem action='SpiralT0Action' />"
380         "    <separator />"
381         "    <toolitem action='SpiralResetAction' />"
382         "  </toolbar>"
384         "  <toolbar name='PenToolbar'>"
385         "    <toolitem action='FreehandModeActionPen' />"
386         "    <separator />"
387         "    <toolitem action='SetPenShapeAction'/>"
388         "  </toolbar>"
390         "  <toolbar name='PencilToolbar'>"
391         "    <toolitem action='FreehandModeActionPencil' />"
392         "    <separator />"
393         "    <toolitem action='PencilToleranceAction' />"
394         "    <separator />"
395         "    <toolitem action='PencilResetAction' />"
396         "    <separator />"
397         "    <toolitem action='SetPencilShapeAction'/>"
398         "  </toolbar>"
400         "  <toolbar name='CalligraphyToolbar'>"
401         "    <separator />"
402         "    <toolitem action='SetProfileAction'/>"
403         "    <separator />"
404         "    <toolitem action='CalligraphyWidthAction' />"
405         "    <toolitem action='PressureAction' />"
406         "    <toolitem action='TraceAction' />"
407         "    <toolitem action='ThinningAction' />"
408         "    <separator />"
409         "    <toolitem action='AngleAction' />"
410         "    <toolitem action='TiltAction' />"
411         "    <toolitem action='FixationAction' />"
412         "    <separator />"
413         "    <toolitem action='CapRoundingAction' />"
414         "    <separator />"
415         "    <toolitem action='TremorAction' />"
416         "    <toolitem action='WiggleAction' />"
417         "    <toolitem action='MassAction' />"
418         "    <separator />"
419         "  </toolbar>"
421         "  <toolbar name='ArcToolbar'>"
422         "    <toolitem action='ArcStateAction' />"
423         "    <separator />"
424         "    <toolitem action='ArcStartAction' />"
425         "    <toolitem action='ArcEndAction' />"
426         "    <separator />"
427         "    <toolitem action='ArcOpenAction' />"
428         "    <separator />"
429         "    <toolitem action='ArcResetAction' />"
430         "    <separator />"
431         "  </toolbar>"
433         "  <toolbar name='PaintbucketToolbar'>"
434         "    <toolitem action='ChannelsAction' />"
435         "    <separator />"
436         "    <toolitem action='ThresholdAction' />"
437         "    <separator />"
438         "    <toolitem action='OffsetAction' />"
439         "    <toolitem action='PaintbucketUnitsAction' />"
440         "    <separator />"
441         "    <toolitem action='AutoGapAction' />"
442         "    <separator />"
443         "    <toolitem action='PaintbucketResetAction' />"
444         "  </toolbar>"
446         "  <toolbar name='EraserToolbar'>"
447         "    <toolitem action='EraserWidthAction' />"
448         "    <separator />"
449         "    <toolitem action='EraserModeAction' />"
450         "  </toolbar>"
452         "  <toolbar name='LPEToolToolbar'>"
453         "    <toolitem action='LPEToolModeAction' />"
454         "    <separator />"
455         "    <toolitem action='LPEShowBBoxAction' />"
456         "    <toolitem action='LPEBBoxFromSelectionAction' />"
457         "    <separator />"
458         "    <toolitem action='LPELineSegmentAction' />"
459         "    <separator />"
460         "    <toolitem action='LPEMeasuringAction' />"
461         "    <toolitem action='LPEToolUnitsAction' />"
462         "    <separator />"
463         "    <toolitem action='LPEOpenLPEDialogAction' />"
464         "  </toolbar>"
466         "  <toolbar name='DropperToolbar'>"
467         "    <toolitem action='DropperOpacityAction' />"
468         "    <toolitem action='DropperPickAlphaAction' />"
469         "    <toolitem action='DropperSetAlphaAction' />"
470         "  </toolbar>"
472         "  <toolbar name='ConnectorToolbar'>"
473         "    <toolitem action='ConnectorEditModeAction' />"
474         "    <toolitem action='ConnectorAvoidAction' />"
475         "    <toolitem action='ConnectorIgnoreAction' />"
476         "    <toolitem action='ConnectorOrthogonalAction' />"
477         "    <toolitem action='ConnectorCurvatureAction' />"
478         "    <toolitem action='ConnectorSpacingAction' />"
479         "    <toolitem action='ConnectorGraphAction' />"
480         "    <toolitem action='ConnectorLengthAction' />"
481         "    <toolitem action='ConnectorDirectedAction' />"
482         "    <toolitem action='ConnectorOverlapAction' />"
483         "    <toolitem action='ConnectorNewConnPointAction' />"
484         "    <toolitem action='ConnectorRemoveConnPointAction' />"
485         "  </toolbar>"
487         "</ui>"
490 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
492 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
494 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
495 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
497 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
498 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
500 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
501 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
503 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
504                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
505                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
507 class VerbAction : public Gtk::Action {
508 public:
509     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511     virtual ~VerbAction();
512     virtual void set_active(bool active = true);
514 protected:
515     virtual Gtk::Widget* create_menu_item_vfunc();
516     virtual Gtk::Widget* create_tool_item_vfunc();
518     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
519     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
521     virtual void on_activate();
523 private:
524     Inkscape::Verb* verb;
525     Inkscape::Verb* verb2;
526     Inkscape::UI::View::View *view;
527     GtkTooltips *tooltips;
528     bool active;
530     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
531 };
534 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
536     Glib::RefPtr<VerbAction> result;
537     SPAction *action = verb->get_action(view);
538     if ( action ) {
539         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
540         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
541     }
543     return result;
546 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
547     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
548     verb(verb),
549     verb2(verb2),
550     view(view),
551     tooltips(tooltips),
552     active(false)
556 VerbAction::~VerbAction()
560 Gtk::Widget* VerbAction::create_menu_item_vfunc()
562 // First call in to get the icon rendered if present in SVG
563     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
564     delete widget;
565     widget = 0;
567     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
568 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
569     return widg;
572 Gtk::Widget* VerbAction::create_tool_item_vfunc()
574 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
575     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
576     GtkWidget* toolbox = 0;
577     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
578                                                                           SP_BUTTON_TYPE_TOGGLE,
579                                                                           verb,
580                                                                           verb2,
581                                                                           view,
582                                                                           tooltips );
583     if ( active ) {
584         sp_button_toggle_set_down( SP_BUTTON(button), active);
585     }
586     gtk_widget_show_all( button );
587     Gtk::Widget* wrapped = Glib::wrap(button);
588     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
589     holder->add(*wrapped);
591 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
592     return holder;
595 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
597 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
598     Gtk::Action::connect_proxy_vfunc(proxy);
601 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
603 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
604     Gtk::Action::disconnect_proxy_vfunc(proxy);
607 void VerbAction::set_active(bool active)
609     this->active = active;
610     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
611     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
612         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
613         if (ti) {
614             // *should* have one child that is the SPButton
615             Gtk::Widget* child = ti->get_child();
616             if ( child && SP_IS_BUTTON(child->gobj()) ) {
617                 SPButton* button = SP_BUTTON(child->gobj());
618                 sp_button_toggle_set_down( button, active );
619             }
620         }
621     }
624 void VerbAction::on_activate()
626     if ( verb ) {
627         SPAction *action = verb->get_action(view);
628         if ( action ) {
629             sp_action_perform(action, 0);
630         }
631     }
634 /* Global text entry widgets necessary for update */
635 /* GtkWidget *dropper_rgb_entry,
636           *dropper_opacity_entry ; */
637 // should be made a private member once this is converted to class
639 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
640     connection->disconnect();
641     delete connection;
644 static void purge_repr_listener( GObject* obj, GObject* tbl )
646     (void)obj;
647     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
648     if (oldrepr) { // remove old listener
649         sp_repr_remove_listener_by_data(oldrepr, tbl);
650         Inkscape::GC::release(oldrepr);
651         oldrepr = 0;
652         g_object_set_data( tbl, "repr", NULL );
653     }
656 GtkWidget *
657 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
658                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
659                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
661     SPAction *action = verb->get_action(view);
662     if (!action) return NULL;
664     SPAction *doubleclick_action;
665     if (doubleclick_verb)
666         doubleclick_action = doubleclick_verb->get_action(view);
667     else
668         doubleclick_action = NULL;
670     /* fixme: Handle sensitive/unsensitive */
671     /* fixme: Implement sp_button_new_from_action */
672     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
673     gtk_widget_show(b);
676     unsigned int shortcut = sp_shortcut_get_primary(verb);
677     if (shortcut) {
678         gchar key[256];
679         sp_ui_shortcut_string(shortcut, key);
680         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
681         if ( t ) {
682             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
683         }
684         g_free(tip);
685     } else {
686         if ( t ) {
687             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
688         }
689     }
691     return b;
695 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
697     SPAction* targetAction = SP_ACTION(user_data);
698     if ( targetAction ) {
699         sp_action_perform( targetAction, NULL );
700     }
703 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
705     if ( data ) {
706         GtkAction* act = GTK_ACTION(data);
707         gtk_action_set_sensitive( act, sensitive );
708     }
711 static SPActionEventVector action_event_vector = {
712     {NULL},
713     NULL,
714     NULL,
715     sp_action_action_set_sensitive,
716     NULL,
717     NULL
718 };
720 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
722     GtkAction* act = 0;
724     SPAction* targetAction = verb->get_action(view);
725     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
726     act = GTK_ACTION(inky);
727     gtk_action_set_sensitive( act, targetAction->sensitive );
729     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
731     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
732     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
734     return act;
737 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
739     Inkscape::UI::View::View *view = desktop;
740     gint verbsToUse[] = {
741         // disabled until we have icons for them:
742         //find
743         //SP_VERB_EDIT_TILE,
744         //SP_VERB_EDIT_UNTILE,
745         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
746         SP_VERB_DIALOG_DISPLAY,
747         SP_VERB_DIALOG_FILL_STROKE,
748         SP_VERB_DIALOG_NAMEDVIEW,
749         SP_VERB_DIALOG_TEXT,
750         SP_VERB_DIALOG_XML_EDITOR,
751         SP_VERB_DIALOG_LAYERS,
752         SP_VERB_EDIT_CLONE,
753         SP_VERB_EDIT_COPY,
754         SP_VERB_EDIT_CUT,
755         SP_VERB_EDIT_DUPLICATE,
756         SP_VERB_EDIT_PASTE,
757         SP_VERB_EDIT_REDO,
758         SP_VERB_EDIT_UNDO,
759         SP_VERB_EDIT_UNLINK_CLONE,
760         SP_VERB_FILE_EXPORT,
761         SP_VERB_FILE_IMPORT,
762         SP_VERB_FILE_NEW,
763         SP_VERB_FILE_OPEN,
764         SP_VERB_FILE_PRINT,
765         SP_VERB_FILE_SAVE,
766         SP_VERB_OBJECT_TO_CURVE,
767         SP_VERB_SELECTION_GROUP,
768         SP_VERB_SELECTION_OUTLINE,
769         SP_VERB_SELECTION_UNGROUP,
770         SP_VERB_ZOOM_1_1,
771         SP_VERB_ZOOM_1_2,
772         SP_VERB_ZOOM_2_1,
773         SP_VERB_ZOOM_DRAWING,
774         SP_VERB_ZOOM_IN,
775         SP_VERB_ZOOM_NEXT,
776         SP_VERB_ZOOM_OUT,
777         SP_VERB_ZOOM_PAGE,
778         SP_VERB_ZOOM_PAGE_WIDTH,
779         SP_VERB_ZOOM_PREV,
780         SP_VERB_ZOOM_SELECTION,
781     };
783     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
785     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
786     Glib::RefPtr<Gtk::ActionGroup> mainActions;
787     if ( groups.find(desktop) != groups.end() ) {
788         mainActions = groups[desktop];
789     }
791     if ( !mainActions ) {
792         mainActions = Gtk::ActionGroup::create("main");
793         groups[desktop] = mainActions;
794     }
796     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
797         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
798         if ( verb ) {
799             if (!mainActions->get_action(verb->get_id())) {
800                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
801                 mainActions->add(Glib::wrap(act));
802             }
803         }
804     }
806     if ( !mainActions->get_action("ToolZoom") ) {
807         GtkTooltips *tt = gtk_tooltips_new();
808         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
809             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
810             if ( va ) {
811                 mainActions->add(va);
812                 if ( i == 0 ) {
813                     va->set_active(true);
814                 }
815             }
816         }
817     }
820     return mainActions;
824 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
826     gtk_widget_set_size_request( widget,
827                                  widget->allocation.width,
828                                  widget->allocation.height );
831 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
833     gtk_widget_set_size_request( widget, -1, -1 );
838 static GtkWidget* toolboxNewCommon( GtkWidget* tb, GtkPositionType handlePos )
840     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
842     gtk_widget_set_sensitive(tb, FALSE);
844     GtkWidget *hb = gtk_handle_box_new();
845     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
846     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
847     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
849     gtk_container_add(GTK_CONTAINER(hb), tb);
850     gtk_widget_show(GTK_WIDGET(tb));
852     sigc::connection* conn = new sigc::connection;
853     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
855     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
856     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
858     return hb;
861 GtkWidget *sp_tool_toolbox_new()
863     GtkWidget *tb = gtk_toolbar_new();
864     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
865     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
867     return toolboxNewCommon( tb, GTK_POS_TOP );
870 GtkWidget *sp_aux_toolbox_new()
872     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
874     return toolboxNewCommon( tb, GTK_POS_LEFT );
877 //####################################
878 //# Commands Bar
879 //####################################
881 GtkWidget *sp_commands_toolbox_new()
883     GtkWidget *tb = gtk_toolbar_new();
885     return toolboxNewCommon( tb, GTK_POS_LEFT );
888 GtkWidget *sp_snap_toolbox_new()
890     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
892     return toolboxNewCommon( tb, GTK_POS_LEFT );
895 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
896                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
897                                                        Glib::ustring const &path, gdouble def,
898                                                        GtkWidget *focusTarget,
899                                                        GtkWidget *us,
900                                                        GObject *dataKludge,
901                                                        gboolean altx, gchar const *altx_mark,
902                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
903                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
904                                                        void (*callback)(GtkAdjustment *, GObject *),
905                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
907     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
908     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
909                                                              lower, upper, step, page, 0 ) );
910     if (us) {
911         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
912     }
914     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
916     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
917     if ( shortLabel ) {
918         g_object_set( act, "short_label", shortLabel, NULL );
919     }
921     if ( (descrCount > 0) && descrLabels && descrValues ) {
922         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
923     }
925     if ( focusTarget ) {
926         ege_adjustment_action_set_focuswidget( act, focusTarget );
927     }
929     if ( altx && altx_mark ) {
930         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
931     }
933     if ( dataKludge ) {
934         // Rather lame, but it's the only place where we need to get the entry name
935         // but we don't have an Entry
936         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
937     }
939     // Using a cast just to make sure we pass in the right kind of function pointer
940     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
942     return act;
946 //####################################
947 //# node editing callbacks
948 //####################################
950 /**
951  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
952  */
953 static ShapeEditor *get_current_shape_editor()
955     if (!SP_ACTIVE_DESKTOP) {
956         return NULL;
957     }
959     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
961     if (!SP_IS_NODE_CONTEXT(event_context)) {
962         return NULL;
963     }
965     return event_context->shape_editor;
969 void
970 sp_node_path_edit_add(void)
972     ShapeEditor *shape_editor = get_current_shape_editor();
973     if (shape_editor) shape_editor->add_node();
976 void
977 sp_node_path_edit_delete(void)
979     ShapeEditor *shape_editor = get_current_shape_editor();
980     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
983 void
984 sp_node_path_edit_delete_segment(void)
986     ShapeEditor *shape_editor = get_current_shape_editor();
987     if (shape_editor) shape_editor->delete_segment();
990 void
991 sp_node_path_edit_break(void)
993     ShapeEditor *shape_editor = get_current_shape_editor();
994     if (shape_editor) shape_editor->break_at_nodes();
997 void
998 sp_node_path_edit_join(void)
1000     ShapeEditor *shape_editor = get_current_shape_editor();
1001     if (shape_editor) shape_editor->join_nodes();
1004 void
1005 sp_node_path_edit_join_segment(void)
1007     ShapeEditor *shape_editor = get_current_shape_editor();
1008     if (shape_editor) shape_editor->join_segments();
1011 void
1012 sp_node_path_edit_toline(void)
1014     ShapeEditor *shape_editor = get_current_shape_editor();
1015     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1018 void
1019 sp_node_path_edit_tocurve(void)
1021     ShapeEditor *shape_editor = get_current_shape_editor();
1022     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1025 void
1026 sp_node_path_edit_cusp(void)
1028     ShapeEditor *shape_editor = get_current_shape_editor();
1029     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1032 void
1033 sp_node_path_edit_smooth(void)
1035     ShapeEditor *shape_editor = get_current_shape_editor();
1036     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1039 void
1040 sp_node_path_edit_symmetrical(void)
1042     ShapeEditor *shape_editor = get_current_shape_editor();
1043     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1046 void
1047 sp_node_path_edit_auto(void)
1049     ShapeEditor *shape_editor = get_current_shape_editor();
1050     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1053 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1054     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1055     bool show = gtk_toggle_action_get_active( act );
1056     prefs->setBool("/tools/nodes/show_handles",  show);
1057     ShapeEditor *shape_editor = get_current_shape_editor();
1058     if (shape_editor) shape_editor->show_handles(show);
1061 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1062     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1063     bool show = gtk_toggle_action_get_active( act );
1064     prefs->setBool("/tools/nodes/show_helperpath",  show);
1065     ShapeEditor *shape_editor = get_current_shape_editor();
1066     if (shape_editor) shape_editor->show_helperpath(show);
1069 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1070     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1073 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1074     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1077 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1078     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1081 /* is called when the node selection is modified */
1082 static void
1083 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1085     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1086     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1087     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1088     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1090     // quit if run by the attr_changed listener
1091     if (g_object_get_data( tbl, "freeze" )) {
1092         return;
1093     }
1095     // in turn, prevent listener from responding
1096     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1098     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1099     SPUnit const *unit = tracker->getActiveUnit();
1101     ShapeEditor *shape_editor = get_current_shape_editor();
1102     if (shape_editor && shape_editor->has_nodepath()) {
1103         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1104         int n_selected = 0;
1105         if (nodepath) {
1106             n_selected = nodepath->numSelected();
1107         }
1109         if (n_selected == 0) {
1110             gtk_action_set_sensitive(xact, FALSE);
1111             gtk_action_set_sensitive(yact, FALSE);
1112         } else {
1113             gtk_action_set_sensitive(xact, TRUE);
1114             gtk_action_set_sensitive(yact, TRUE);
1115             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1116             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1118             if (n_selected == 1) {
1119                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1120                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1121                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1122                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1123                 }
1124             } else {
1125                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1126                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1127                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1128                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1129                        selected nodes don't coincide (in this case we use the coordinates of the center
1130                        of the bounding box). So the entries are never set to zero. */
1131                     // FIXME: Maybe we should clear the entry if several nodes are selected
1132                     //        instead of providing a kind of average value
1133                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1134                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1135                 }
1136             }
1137         }
1138     } else {
1139         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1140         gtk_action_set_sensitive(xact, FALSE);
1141         gtk_action_set_sensitive(yact, FALSE);
1142     }
1144     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1147 static void
1148 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1150     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1151     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1153     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1154     SPUnit const *unit = tracker->getActiveUnit();
1156     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1157         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1158     }
1160     // quit if run by the attr_changed listener
1161     if (g_object_get_data( tbl, "freeze" )) {
1162         return;
1163     }
1165     // in turn, prevent listener from responding
1166     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1168     ShapeEditor *shape_editor = get_current_shape_editor();
1169     if (shape_editor && shape_editor->has_nodepath()) {
1170         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1171         if (!strcmp(value_name, "x")) {
1172             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1173         }
1174         if (!strcmp(value_name, "y")) {
1175             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1176         }
1177     }
1179     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1182 static void
1183 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1185     sp_node_path_value_changed(adj, tbl, "x");
1188 static void
1189 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1191     sp_node_path_value_changed(adj, tbl, "y");
1194 void
1195 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1197     {
1198     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1199     SPItem *item = selection->singleItem();
1200     if (item && SP_IS_LPE_ITEM(item)) {
1201        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1202            gtk_action_set_sensitive(w, TRUE);
1203        } else {
1204            gtk_action_set_sensitive(w, FALSE);
1205        }
1206     } else {
1207        gtk_action_set_sensitive(w, FALSE);
1208     }
1209     }
1211     {
1212     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1213     SPItem *item = selection->singleItem();
1214     if (item && item->clip_ref && item->clip_ref->getObject()) {
1215        gtk_action_set_sensitive(w, TRUE);
1216     } else {
1217        gtk_action_set_sensitive(w, FALSE);
1218     }
1219     }
1221     {
1222     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1223     SPItem *item = selection->singleItem();
1224     if (item && item->mask_ref && item->mask_ref->getObject()) {
1225        gtk_action_set_sensitive(w, TRUE);
1226     } else {
1227        gtk_action_set_sensitive(w, FALSE);
1228     }
1229     }
1232 void
1233 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1235     sp_node_toolbox_sel_changed (selection, tbl);
1240 //################################
1241 //##    Node Editing Toolbox    ##
1242 //################################
1244 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1246     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1247     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1248     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1249     g_object_set_data( holder, "tracker", tracker );
1251     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1253     {
1254         InkAction* inky = ink_action_new( "NodeInsertAction",
1255                                           _("Insert node"),
1256                                           _("Insert new nodes into selected segments"),
1257                                           INKSCAPE_ICON_NODE_ADD,
1258                                           secondarySize );
1259         g_object_set( inky, "short_label", _("Insert"), NULL );
1260         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1261         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1262     }
1264     {
1265         InkAction* inky = ink_action_new( "NodeDeleteAction",
1266                                           _("Delete node"),
1267                                           _("Delete selected nodes"),
1268                                           INKSCAPE_ICON_NODE_DELETE,
1269                                           secondarySize );
1270         g_object_set( inky, "short_label", _("Delete"), NULL );
1271         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1272         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273     }
1275     {
1276         InkAction* inky = ink_action_new( "NodeJoinAction",
1277                                           _("Join endnodes"),
1278                                           _("Join selected endnodes"),
1279                                           INKSCAPE_ICON_NODE_JOIN,
1280                                           secondarySize );
1281         g_object_set( inky, "short_label", _("Join"), NULL );
1282         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1283         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284     }
1286     {
1287         InkAction* inky = ink_action_new( "NodeBreakAction",
1288                                           _("Break nodes"),
1289                                           _("Break path at selected nodes"),
1290                                           INKSCAPE_ICON_NODE_BREAK,
1291                                           secondarySize );
1292         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1293         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294     }
1297     {
1298         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1299                                           _("Join with segment"),
1300                                           _("Join selected endnodes with a new segment"),
1301                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1302                                           secondarySize );
1303         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1304         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1305     }
1307     {
1308         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1309                                           _("Delete segment"),
1310                                           _("Delete segment between two non-endpoint nodes"),
1311                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1312                                           secondarySize );
1313         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1314         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315     }
1317     {
1318         InkAction* inky = ink_action_new( "NodeCuspAction",
1319                                           _("Node Cusp"),
1320                                           _("Make selected nodes corner"),
1321                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1322                                           secondarySize );
1323         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1324         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1325     }
1327     {
1328         InkAction* inky = ink_action_new( "NodeSmoothAction",
1329                                           _("Node Smooth"),
1330                                           _("Make selected nodes smooth"),
1331                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1332                                           secondarySize );
1333         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1334         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1335     }
1337     {
1338         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1339                                           _("Node Symmetric"),
1340                                           _("Make selected nodes symmetric"),
1341                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1342                                           secondarySize );
1343         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1344         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1345     }
1347     {
1348         InkAction* inky = ink_action_new( "NodeAutoAction",
1349                                           _("Node Auto"),
1350                                           _("Make selected nodes auto-smooth"),
1351                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1352                                           secondarySize );
1353         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1354         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1355     }
1357     {
1358         InkAction* inky = ink_action_new( "NodeLineAction",
1359                                           _("Node Line"),
1360                                           _("Make selected segments lines"),
1361                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1362                                           secondarySize );
1363         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1364         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365     }
1367     {
1368         InkAction* inky = ink_action_new( "NodeCurveAction",
1369                                           _("Node Curve"),
1370                                           _("Make selected segments curves"),
1371                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1372                                           secondarySize );
1373         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1374         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375     }
1377     {
1378         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1379                                                       _("Show Handles"),
1380                                                       _("Show the Bezier handles of selected nodes"),
1381                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1382                                                       Inkscape::ICON_SIZE_DECORATION );
1383         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1384         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1385         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1386     }
1388     {
1389         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1390                                                       _("Show Outline"),
1391                                                       _("Show the outline of the path"),
1392                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1393                                                       Inkscape::ICON_SIZE_DECORATION );
1394         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1395         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1396         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1397     }
1399     {
1400         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1401                                           _("Next path effect parameter"),
1402                                           _("Show next path effect parameter for editing"),
1403                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1404                                           Inkscape::ICON_SIZE_DECORATION );
1405         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1406         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1407         g_object_set_data( holder, "nodes_lpeedit", inky);
1408     }
1410     {
1411         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1412                                           _("Edit clipping path"),
1413                                           _("Edit the clipping path of the object"),
1414                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1415                                           Inkscape::ICON_SIZE_DECORATION );
1416         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418         g_object_set_data( holder, "nodes_clippathedit", inky);
1419     }
1421     {
1422         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1423                                           _("Edit mask path"),
1424                                           _("Edit the mask of the object"),
1425                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1426                                           Inkscape::ICON_SIZE_DECORATION );
1427         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429         g_object_set_data( holder, "nodes_maskedit", inky);
1430     }
1432     /* X coord of selected node(s) */
1433     {
1434         EgeAdjustmentAction* eact = 0;
1435         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1436         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1437         eact = create_adjustment_action( "NodeXAction",
1438                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1439                                          "/tools/nodes/Xcoord", 0,
1440                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1441                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1442                                          labels, values, G_N_ELEMENTS(labels),
1443                                          sp_node_path_x_value_changed );
1444         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1445         g_object_set_data( holder, "nodes_x_action", eact );
1446         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1447         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1448     }
1450     /* Y coord of selected node(s) */
1451     {
1452         EgeAdjustmentAction* eact = 0;
1453         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1454         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1455         eact = create_adjustment_action( "NodeYAction",
1456                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1457                                          "/tools/nodes/Ycoord", 0,
1458                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1459                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1460                                          labels, values, G_N_ELEMENTS(labels),
1461                                          sp_node_path_y_value_changed );
1462         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1463         g_object_set_data( holder, "nodes_y_action", eact );
1464         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1465         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1466     }
1468     // add the units menu
1469     {
1470         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1471         gtk_action_group_add_action( mainActions, act );
1472     }
1475     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1477     //watch selection
1478     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1480     sigc::connection *c_selection_changed =
1481         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1482                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1483     pool->add_connection ("selection-changed", c_selection_changed);
1485     sigc::connection *c_selection_modified =
1486         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1487                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1488     pool->add_connection ("selection-modified", c_selection_modified);
1490     sigc::connection *c_subselection_changed =
1491         new sigc::connection (desktop->connectToolSubselectionChanged
1492                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1493     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1495     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1497     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1498 } // end of sp_node_toolbox_prep()
1501 //########################
1502 //##    Zoom Toolbox    ##
1503 //########################
1505 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1507     // no custom GtkAction setup needed
1508 } // end of sp_zoom_toolbox_prep()
1510 void
1511 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1513     toolbox_set_desktop(toolbox,
1514                         desktop,
1515                         setup_tool_toolbox,
1516                         update_tool_toolbox,
1517                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1518                                                                          "event_context_connection")));
1522 void
1523 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1525     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1526                         desktop,
1527                         setup_aux_toolbox,
1528                         update_aux_toolbox,
1529                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1530                                                                          "event_context_connection")));
1533 void
1534 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1536     toolbox_set_desktop(toolbox,
1537                         desktop,
1538                         setup_commands_toolbox,
1539                         update_commands_toolbox,
1540                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1541                                                                          "event_context_connection")));
1544 void
1545 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1547     toolbox_set_desktop(toolbox,
1548                         desktop,
1549                         setup_snap_toolbox,
1550                         update_snap_toolbox,
1551                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1552                                                                          "event_context_connection")));
1556 static void
1557 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1559     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1560     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1562     if (old_desktop) {
1563         GList *children, *iter;
1565         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1566         for ( iter = children ; iter ; iter = iter->next ) {
1567             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1568         }
1569         g_list_free(children);
1570     }
1572     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1574     if (desktop) {
1575         gtk_widget_set_sensitive(toolbox, TRUE);
1576         setup_func(toolbox, desktop);
1577         update_func(desktop, desktop->event_context, toolbox);
1578         *conn = desktop->connectEventContextChanged
1579             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1580     } else {
1581         gtk_widget_set_sensitive(toolbox, FALSE);
1582     }
1584 } // end of toolbox_set_desktop()
1587 static void
1588 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1590     gchar const * descr =
1591         "<ui>"
1592         "  <toolbar name='ToolToolbar'>"
1593         "    <toolitem action='ToolSelector' />"
1594         "    <toolitem action='ToolNode' />"
1595         "    <toolitem action='ToolTweak' />"
1596         "    <toolitem action='ToolSpray' />"
1597         "    <toolitem action='ToolZoom' />"
1598         "    <toolitem action='ToolRect' />"
1599         "    <toolitem action='Tool3DBox' />"
1600         "    <toolitem action='ToolArc' />"
1601         "    <toolitem action='ToolStar' />"
1602         "    <toolitem action='ToolSpiral' />"
1603         "    <toolitem action='ToolPencil' />"
1604         "    <toolitem action='ToolPen' />"
1605         "    <toolitem action='ToolCalligraphic' />"
1606         "    <toolitem action='ToolEraser' />"
1607 //        "    <toolitem action='ToolLPETool' />"
1608         "    <toolitem action='ToolPaintBucket' />"
1609         "    <toolitem action='ToolText' />"
1610         "    <toolitem action='ToolConnector' />"
1611         "    <toolitem action='ToolGradient' />"
1612         "    <toolitem action='ToolDropper' />"
1613         "  </toolbar>"
1614         "</ui>";
1615     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1616     GtkUIManager* mgr = gtk_ui_manager_new();
1617     GError* errVal = 0;
1618     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1620     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1621     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1623     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1624     if ( prefs->getBool("/toolbox/icononly", true) ) {
1625         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1626     }
1627     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1628     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1630     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1631     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1633     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1635     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1636     if ( child ) {
1637         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1638     }
1640     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1641 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1645 static void
1646 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1648     gchar const *const tname = ( eventcontext
1649                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1650                                  : NULL );
1651     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1653     for (int i = 0 ; tools[i].type_name ; i++ ) {
1654         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1655         if ( act ) {
1656             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1657             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1658             if ( verbAct ) {
1659                 verbAct->set_active(setActive);
1660             }
1661         }
1662     }
1665 static void
1666 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1668     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1669     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1670     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1671     GtkUIManager* mgr = gtk_ui_manager_new();
1672     GError* errVal = 0;
1673     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1674     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1676     std::map<std::string, GtkWidget*> dataHolders;
1678     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1679         if ( aux_toolboxes[i].prep_func ) {
1680             // converted to GtkActions and UIManager
1682             GtkWidget* kludge = gtk_toolbar_new();
1683             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1684             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1685             dataHolders[aux_toolboxes[i].type_name] = kludge;
1686             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1687         } else {
1689             GtkWidget *sub_toolbox = 0;
1690             if (aux_toolboxes[i].create_func == NULL)
1691                 sub_toolbox = sp_empty_toolbox_new(desktop);
1692             else {
1693                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1694             }
1696             gtk_size_group_add_widget( grouper, sub_toolbox );
1698             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1699             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1701         }
1702     }
1704     // Second pass to create toolbars *after* all GtkActions are created
1705     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1706         if ( aux_toolboxes[i].prep_func ) {
1707             // converted to GtkActions and UIManager
1709             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1711             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1712             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1714             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1715             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1716             g_free( tmp );
1717             tmp = 0;
1719             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1720                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1721             }
1723             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1724             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1726             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1728             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1729                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1730                 swatch->setDesktop( desktop );
1731                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1732                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1733                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1734                 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 );
1735             }
1737             gtk_widget_show_all( holder );
1738             sp_set_font_size_smaller( holder );
1740             gtk_size_group_add_widget( grouper, holder );
1742             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1743             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1744         }
1745     }
1747     g_object_unref( G_OBJECT(grouper) );
1750 static void
1751 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1753     gchar const *tname = ( eventcontext
1754                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1755                            : NULL );
1756     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1757         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1758         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1759             gtk_widget_show_all(sub_toolbox);
1760             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1761         } else {
1762             gtk_widget_hide(sub_toolbox);
1763         }
1764     }
1767 static void
1768 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1770     gchar const * descr =
1771         "<ui>"
1772         "  <toolbar name='CommandsToolbar'>"
1773         "    <toolitem action='FileNew' />"
1774         "    <toolitem action='FileOpen' />"
1775         "    <toolitem action='FileSave' />"
1776         "    <toolitem action='FilePrint' />"
1777         "    <separator />"
1778         "    <toolitem action='FileImport' />"
1779         "    <toolitem action='FileExport' />"
1780         "    <separator />"
1781         "    <toolitem action='EditUndo' />"
1782         "    <toolitem action='EditRedo' />"
1783         "    <separator />"
1784         "    <toolitem action='EditCopy' />"
1785         "    <toolitem action='EditCut' />"
1786         "    <toolitem action='EditPaste' />"
1787         "    <separator />"
1788         "    <toolitem action='ZoomSelection' />"
1789         "    <toolitem action='ZoomDrawing' />"
1790         "    <toolitem action='ZoomPage' />"
1791         "    <separator />"
1792         "    <toolitem action='EditDuplicate' />"
1793         "    <toolitem action='EditClone' />"
1794         "    <toolitem action='EditUnlinkClone' />"
1795         "    <separator />"
1796         "    <toolitem action='SelectionGroup' />"
1797         "    <toolitem action='SelectionUnGroup' />"
1798         "    <separator />"
1799         "    <toolitem action='DialogFillStroke' />"
1800         "    <toolitem action='DialogText' />"
1801         "    <toolitem action='DialogLayers' />"
1802         "    <toolitem action='DialogXMLEditor' />"
1803         "    <toolitem action='DialogAlignDistribute' />"
1804         "    <separator />"
1805         "    <toolitem action='DialogPreferences' />"
1806         "    <toolitem action='DialogDocumentProperties' />"
1807         "  </toolbar>"
1808         "</ui>";
1809     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1810     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1812     GtkUIManager* mgr = gtk_ui_manager_new();
1813     GError* errVal = 0;
1815     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1816     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1818     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1819     if ( prefs->getBool("/toolbox/icononly", true) ) {
1820         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1821     }
1823     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1824     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1826     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1827     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1830     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1832     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1833     if ( child ) {
1834         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1835     }
1837     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1840 static void
1841 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1845 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1847     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1848         return;
1849     }
1851     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1852     g_assert(ptr != NULL);
1854     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1855     SPNamedView *nv = sp_desktop_namedview(dt);
1856     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1858     if (dt == NULL || nv == NULL) {
1859         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1860         return;
1861     }
1863     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1865     if (repr == NULL) {
1866         g_warning("This namedview doesn't have a xml representation attached!");
1867         return;
1868     }
1870     bool saved = sp_document_get_undo_sensitive(doc);
1871     sp_document_set_undo_sensitive(doc, false);
1873     bool v = false;
1874     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1876     switch (attr) {
1877         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1878             dt->toggleSnapGlobal();
1879             break;
1880         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1881             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1882             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1883             break;
1884         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1885             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1886             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1887             break;
1888         case SP_ATTR_INKSCAPE_BBOX_NODES:
1889             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1890             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1891             break;
1892         case SP_ATTR_INKSCAPE_SNAP_NODES:
1893             v = nv->snap_manager.snapprefs.getSnapModeNode();
1894             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1895             break;
1896         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1897             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1898             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1899             break;
1900         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1901             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1902             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1903             break;
1904         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1905             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1906             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1907             break;
1908         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1909             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1910             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1911             break;
1912         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1913             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1914             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1915             break;
1916         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1917             v = nv->snap_manager.snapprefs.getSnapToGrids();
1918             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1919             break;
1920         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1921             v = nv->snap_manager.snapprefs.getSnapToGuides();
1922             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1923             break;
1924         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1925             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1926             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1927             break;
1928             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1929               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1930               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1931               break;*/
1932         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1933             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1934             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1935             break;
1936         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1937             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1938             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1939             break;
1940         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1941             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1942             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1943             break;
1944         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1945             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1946             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1947             break;
1948         default:
1949             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1950             break;
1951     }
1953     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1954     doc->setModifiedSinceSave();
1956     sp_document_set_undo_sensitive(doc, saved);
1959 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1961     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1962     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1964     gchar const * descr =
1965         "<ui>"
1966         "  <toolbar name='SnapToolbar'>"
1967         "    <toolitem action='ToggleSnapGlobal' />"
1968         "    <separator />"
1969         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
1970         "    <toolitem action='ToggleSnapToBBoxPath' />"
1971         "    <toolitem action='ToggleSnapToBBoxNode' />"
1972         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1973         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
1974         "    <separator />"
1975         "    <toolitem action='ToggleSnapFromNode' />"
1976         "    <toolitem action='ToggleSnapToItemPath' />"
1977         "    <toolitem action='ToggleSnapToPathIntersections' />"
1978         "    <toolitem action='ToggleSnapToItemNode' />"
1979         "    <toolitem action='ToggleSnapToSmoothNodes' />"
1980         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
1981         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
1982         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
1983         "    <separator />"
1984         "    <toolitem action='ToggleSnapToPageBorder' />"
1985         "    <toolitem action='ToggleSnapToGrids' />"
1986         "    <toolitem action='ToggleSnapToGuides' />"
1987         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
1988         "  </toolbar>"
1989         "</ui>";
1991     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1993     {
1994         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
1995                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
1996                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
1998         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
1999         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2000     }
2002     {
2003         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2004                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2005                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2007         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2008         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2009     }
2011     {
2012         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2013                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2014                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2016         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2017         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2018     }
2020     {
2021         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2022                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2023                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2025         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2026         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2027     }
2029     {
2030         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2031                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2032                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2033                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2035         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2036         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2037     }
2039     {
2040         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2041                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2042                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2044         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2045         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2046     }
2048     {
2049         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2050                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2052         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2053         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2054     }
2056     {
2057         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2058                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2059                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2061         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2062         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2063     }
2065     {
2066         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2067                                                      _("Path intersections"), _("Snap to path intersections"),
2068                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2070         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2071         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2072     }
2074     {
2075         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2076                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2077                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2079         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2080         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2081     }
2083     {
2084         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2085                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2086                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2088         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2089         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2090     }
2092     {
2093         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2094                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2095                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2097         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2098         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2099     }
2101     {
2102         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2103                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2104                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2106         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2107         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2108     }
2110     {
2111         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2112                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2113                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2115         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2116         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2117     }
2119     {
2120         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2121                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2122                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2124         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2125         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2126     }
2128     {
2129         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2130                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2131                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2133         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2134         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2135     }
2137     {
2138         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2139                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2140                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2142         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2143         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2144     }
2146     /*{
2147       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2148       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2149       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2150       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2152       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2153       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2154       }*/
2156     GtkUIManager* mgr = gtk_ui_manager_new();
2157     GError* errVal = 0;
2159     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2160     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2162     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2163     if ( prefs->getBool("/toolbox/icononly", true) ) {
2164         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2165     }
2167     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2168     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2170     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2171     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2173     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2175     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2176     if ( child ) {
2177         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2178     }
2180     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2184 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2186     g_assert(desktop != NULL);
2187     g_assert(toolbox != NULL);
2189     SPNamedView *nv = sp_desktop_namedview(desktop);
2190     if (nv == NULL) {
2191         g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2192         return;
2193     }
2195     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2197     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2198     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2199     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2200     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2201     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2202     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2203     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2204     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2205     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2206     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2207     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2208     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2209     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2210     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2211     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2212     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2213     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2214     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2217     if (!act1) {
2218         return; // The snap actions haven't been defined yet (might be the case during startup)
2219     }
2221     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2222     // changes in our document because we're only updating the UI;
2223     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2224     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2226     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2227     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2229     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2230     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2231     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2233     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2234     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2235     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2236     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2237     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2238     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2239     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2240     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2242     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2243     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2244     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2246     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2247     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2248     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2249     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2250     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2251     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2252     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2253     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2254     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2255     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2256     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2257     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2258     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2259     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2260     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2262     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2263     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2264     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2265     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2267     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2268     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2269     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2270     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2273     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2276 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2278     gtk_widget_show(toolbox_toplevel);
2279     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2281     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2282     if (!shown_toolbox) {
2283         return;
2284     }
2285     gtk_widget_show(toolbox);
2287     gtk_widget_show_all(shown_toolbox);
2290 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2292     GtkWidget *tbl = gtk_toolbar_new();
2293     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2294     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2296     gtk_widget_show_all(tbl);
2297     sp_set_font_size_smaller (tbl);
2299     return tbl;
2302 #define MODE_LABEL_WIDTH 70
2304 //########################
2305 //##       Star         ##
2306 //########################
2308 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2310     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2312     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2313         // do not remember prefs if this call is initiated by an undo change, because undoing object
2314         // creation sets bogus values to its attributes before it is deleted
2315         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2316         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2317     }
2319     // quit if run by the attr_changed listener
2320     if (g_object_get_data( dataKludge, "freeze" )) {
2321         return;
2322     }
2324     // in turn, prevent listener from responding
2325     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2327     bool modmade = false;
2329     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2330     GSList const *items = selection->itemList();
2331     for (; items != NULL; items = items->next) {
2332         if (SP_IS_STAR((SPItem *) items->data)) {
2333             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2334             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2335             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2336                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2337                                     + M_PI / (gint)adj->value));
2338             SP_OBJECT((SPItem *) items->data)->updateRepr();
2339             modmade = true;
2340         }
2341     }
2342     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2343                                    _("Star: Change number of corners"));
2345     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2348 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2350     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2352     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2353         if (!IS_NAN(adj->value)) {
2354             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2355             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2356         }
2357     }
2359     // quit if run by the attr_changed listener
2360     if (g_object_get_data( dataKludge, "freeze" )) {
2361         return;
2362     }
2364     // in turn, prevent listener from responding
2365     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2367     bool modmade = false;
2368     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2369     GSList const *items = selection->itemList();
2370     for (; items != NULL; items = items->next) {
2371         if (SP_IS_STAR((SPItem *) items->data)) {
2372             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2374             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2375             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2376             if (r2 < r1) {
2377                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2378             } else {
2379                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2380             }
2382             SP_OBJECT((SPItem *) items->data)->updateRepr();
2383             modmade = true;
2384         }
2385     }
2387     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2388                                    _("Star: Change spoke ratio"));
2390     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2393 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2395     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2396     bool flat = ege_select_one_action_get_active( act ) == 0;
2398     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2399         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2400         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2401     }
2403     // quit if run by the attr_changed listener
2404     if (g_object_get_data( dataKludge, "freeze" )) {
2405         return;
2406     }
2408     // in turn, prevent listener from responding
2409     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2411     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2412     GSList const *items = selection->itemList();
2413     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2414     bool modmade = false;
2416     if ( prop_action ) {
2417         gtk_action_set_sensitive( prop_action, !flat );
2418     }
2420     for (; items != NULL; items = items->next) {
2421         if (SP_IS_STAR((SPItem *) items->data)) {
2422             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2423             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2424             SP_OBJECT((SPItem *) items->data)->updateRepr();
2425             modmade = true;
2426         }
2427     }
2429     if (modmade) {
2430         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2431                          flat ? _("Make polygon") : _("Make star"));
2432     }
2434     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2437 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2439     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2441     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2442         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2443         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2444     }
2446     // quit if run by the attr_changed listener
2447     if (g_object_get_data( dataKludge, "freeze" )) {
2448         return;
2449     }
2451     // in turn, prevent listener from responding
2452     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2454     bool modmade = false;
2456     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2457     GSList const *items = selection->itemList();
2458     for (; items != NULL; items = items->next) {
2459         if (SP_IS_STAR((SPItem *) items->data)) {
2460             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2461             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2462             SP_OBJECT(items->data)->updateRepr();
2463             modmade = true;
2464         }
2465     }
2466     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2467                                    _("Star: Change rounding"));
2469     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2472 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2474     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2476     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2477         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2478         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2479     }
2481     // quit if run by the attr_changed listener
2482     if (g_object_get_data( dataKludge, "freeze" )) {
2483         return;
2484     }
2486     // in turn, prevent listener from responding
2487     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2489     bool modmade = false;
2491     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2492     GSList const *items = selection->itemList();
2493     for (; items != NULL; items = items->next) {
2494         if (SP_IS_STAR((SPItem *) items->data)) {
2495             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2496             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2497             SP_OBJECT(items->data)->updateRepr();
2498             modmade = true;
2499         }
2500     }
2501     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2502                                    _("Star: Change randomization"));
2504     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2508 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2509                                        gchar const */*old_value*/, gchar const */*new_value*/,
2510                                        bool /*is_interactive*/, gpointer data)
2512     GtkWidget *tbl = GTK_WIDGET(data);
2514     // quit if run by the _changed callbacks
2515     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2516         return;
2517     }
2519     // in turn, prevent callbacks from responding
2520     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2522     GtkAdjustment *adj = 0;
2524     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2525     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2527     if (!strcmp(name, "inkscape:randomized")) {
2528         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2529         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2530     } else if (!strcmp(name, "inkscape:rounded")) {
2531         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2532         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2533     } else if (!strcmp(name, "inkscape:flatsided")) {
2534         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2535         char const *flatsides = repr->attribute("inkscape:flatsided");
2536         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2537         if ( flatsides && !strcmp(flatsides,"false") ) {
2538             ege_select_one_action_set_active( flat_action, 1 );
2539             gtk_action_set_sensitive( prop_action, TRUE );
2540         } else {
2541             ege_select_one_action_set_active( flat_action, 0 );
2542             gtk_action_set_sensitive( prop_action, FALSE );
2543         }
2544     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2545         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2546         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2547         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2548         if (r2 < r1) {
2549             gtk_adjustment_set_value(adj, r2/r1);
2550         } else {
2551             gtk_adjustment_set_value(adj, r1/r2);
2552         }
2553     } else if (!strcmp(name, "sodipodi:sides")) {
2554         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2555         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2556     }
2558     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2562 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2564     NULL, /* child_added */
2565     NULL, /* child_removed */
2566     star_tb_event_attr_changed,
2567     NULL, /* content_changed */
2568     NULL  /* order_changed */
2569 };
2572 /**
2573  *  \param selection Should not be NULL.
2574  */
2575 static void
2576 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2578     int n_selected = 0;
2579     Inkscape::XML::Node *repr = NULL;
2581     purge_repr_listener( tbl, tbl );
2583     for (GSList const *items = selection->itemList();
2584          items != NULL;
2585          items = items->next)
2586     {
2587         if (SP_IS_STAR((SPItem *) items->data)) {
2588             n_selected++;
2589             repr = SP_OBJECT_REPR((SPItem *) items->data);
2590         }
2591     }
2593     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2595     if (n_selected == 0) {
2596         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2597     } else if (n_selected == 1) {
2598         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2600         if (repr) {
2601             g_object_set_data( tbl, "repr", repr );
2602             Inkscape::GC::anchor(repr);
2603             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2604             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2605         }
2606     } else {
2607         // FIXME: implement averaging of all parameters for multiple selected stars
2608         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2609         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2610     }
2614 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2616     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2617     // callbacks to lump all the changes for all selected objects in one undo step
2619     GtkAdjustment *adj = 0;
2621     // fixme: make settable in prefs!
2622     gint mag = 5;
2623     gdouble prop = 0.5;
2624     gboolean flat = FALSE;
2625     gdouble randomized = 0;
2626     gdouble rounded = 0;
2628     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2629     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2631     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2632     gtk_action_set_sensitive( sb2, !flat );
2634     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2635     gtk_adjustment_set_value(adj, mag);
2636     gtk_adjustment_value_changed(adj);
2638     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2639     gtk_adjustment_set_value(adj, prop);
2640     gtk_adjustment_value_changed(adj);
2642     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2643     gtk_adjustment_set_value(adj, rounded);
2644     gtk_adjustment_value_changed(adj);
2646     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2647     gtk_adjustment_set_value(adj, randomized);
2648     gtk_adjustment_value_changed(adj);
2652 void
2653 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2655     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2656     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2657     GtkWidget *l = gtk_label_new(NULL);
2658     gtk_label_set_markup(GTK_LABEL(l), title);
2659     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2660     if ( GTK_IS_TOOLBAR(tbl) ) {
2661         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2662     } else {
2663         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2664     }
2665     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2669 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2671     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2673     {
2674         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2675         ege_output_action_set_use_markup( act, TRUE );
2676         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2677         g_object_set_data( holder, "mode_action", act );
2678     }
2680     {
2681         EgeAdjustmentAction* eact = 0;
2682         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2683         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2685         /* Flatsided checkbox */
2686         {
2687             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2689             GtkTreeIter iter;
2690             gtk_list_store_append( model, &iter );
2691             gtk_list_store_set( model, &iter,
2692                                 0, _("Polygon"),
2693                                 1, _("Regular polygon (with one handle) instead of a star"),
2694                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2695                                 -1 );
2697             gtk_list_store_append( model, &iter );
2698             gtk_list_store_set( model, &iter,
2699                                 0, _("Star"),
2700                                 1, _("Star instead of a regular polygon (with one handle)"),
2701                                 2, INKSCAPE_ICON_DRAW_STAR,
2702                                 -1 );
2704             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2705             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2706             g_object_set_data( holder, "flat_action", act );
2708             ege_select_one_action_set_appearance( act, "full" );
2709             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2710             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2711             ege_select_one_action_set_icon_column( act, 2 );
2712             ege_select_one_action_set_icon_size( act, secondarySize );
2713             ege_select_one_action_set_tooltip_column( act, 1  );
2715             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2716             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2717         }
2719         /* Magnitude */
2720         {
2721         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2722         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2723         eact = create_adjustment_action( "MagnitudeAction",
2724                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2725                                          "/tools/shapes/star/magnitude", 3,
2726                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2727                                          3, 1024, 1, 5,
2728                                          labels, values, G_N_ELEMENTS(labels),
2729                                          sp_stb_magnitude_value_changed,
2730                                          1.0, 0 );
2731         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2732         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2733         }
2735         /* Spoke ratio */
2736         {
2737         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2738         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2739         eact = create_adjustment_action( "SpokeAction",
2740                                          _("Spoke ratio"), _("Spoke ratio:"),
2741                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2742                                          // Base radius is the same for the closest handle.
2743                                          _("Base radius to tip radius ratio"),
2744                                          "/tools/shapes/star/proportion", 0.5,
2745                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2746                                          0.01, 1.0, 0.01, 0.1,
2747                                          labels, values, G_N_ELEMENTS(labels),
2748                                          sp_stb_proportion_value_changed );
2749         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2750         g_object_set_data( holder, "prop_action", eact );
2751         }
2753         if ( !isFlatSided ) {
2754             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2755         } else {
2756             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2757         }
2759         /* Roundedness */
2760         {
2761         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2762         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2763         eact = create_adjustment_action( "RoundednessAction",
2764                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2765                                          "/tools/shapes/star/rounded", 0.0,
2766                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2767                                          -10.0, 10.0, 0.01, 0.1,
2768                                          labels, values, G_N_ELEMENTS(labels),
2769                                          sp_stb_rounded_value_changed );
2770         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2771         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2772         }
2774         /* Randomization */
2775         {
2776         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2777         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2778         eact = create_adjustment_action( "RandomizationAction",
2779                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2780                                          "/tools/shapes/star/randomized", 0.0,
2781                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2782                                          -10.0, 10.0, 0.001, 0.01,
2783                                          labels, values, G_N_ELEMENTS(labels),
2784                                          sp_stb_randomized_value_changed, 0.1, 3 );
2785         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2786         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2787         }
2788     }
2790     {
2791         /* Reset */
2792         {
2793             GtkAction* act = gtk_action_new( "StarResetAction",
2794                                              _("Defaults"),
2795                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2796                                              GTK_STOCK_CLEAR );
2797             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2798             gtk_action_group_add_action( mainActions, act );
2799             gtk_action_set_sensitive( act, TRUE );
2800         }
2801     }
2803     sigc::connection *connection = new sigc::connection(
2804         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2805         );
2806     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2807     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2811 //########################
2812 //##       Rect         ##
2813 //########################
2815 static void sp_rtb_sensitivize( GObject *tbl )
2817     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2818     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2819     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2821     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2822         gtk_action_set_sensitive( not_rounded, FALSE );
2823     } else {
2824         gtk_action_set_sensitive( not_rounded, TRUE );
2825     }
2829 static void
2830 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2831                           void (*setter)(SPRect *, gdouble))
2833     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2835     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2836     SPUnit const *unit = tracker->getActiveUnit();
2838     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2839         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2840         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2841     }
2843     // quit if run by the attr_changed listener
2844     if (g_object_get_data( tbl, "freeze" )) {
2845         return;
2846     }
2848     // in turn, prevent listener from responding
2849     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2851     bool modmade = false;
2852     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2853     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2854         if (SP_IS_RECT(items->data)) {
2855             if (adj->value != 0) {
2856                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2857             } else {
2858                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2859             }
2860             modmade = true;
2861         }
2862     }
2864     sp_rtb_sensitivize( tbl );
2866     if (modmade) {
2867         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2868                                    _("Change rectangle"));
2869     }
2871     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2874 static void
2875 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2877     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2880 static void
2881 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2883     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2886 static void
2887 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2889     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2892 static void
2893 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2895     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2900 static void
2901 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2903     GtkAdjustment *adj = 0;
2905     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2906     gtk_adjustment_set_value(adj, 0.0);
2907     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2908     gtk_adjustment_value_changed(adj);
2910     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2911     gtk_adjustment_set_value(adj, 0.0);
2912     gtk_adjustment_value_changed(adj);
2914     sp_rtb_sensitivize( obj );
2917 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2918                                        gchar const */*old_value*/, gchar const */*new_value*/,
2919                                        bool /*is_interactive*/, gpointer data)
2921     GObject *tbl = G_OBJECT(data);
2923     // quit if run by the _changed callbacks
2924     if (g_object_get_data( tbl, "freeze" )) {
2925         return;
2926     }
2928     // in turn, prevent callbacks from responding
2929     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2931     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2932     SPUnit const *unit = tracker->getActiveUnit();
2934     gpointer item = g_object_get_data( tbl, "item" );
2935     if (item && SP_IS_RECT(item)) {
2936         {
2937             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2938             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2939             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2940         }
2942         {
2943             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2944             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2945             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2946         }
2948         {
2949             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2950             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2951             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2952         }
2954         {
2955             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2956             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2957             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2958         }
2959     }
2961     sp_rtb_sensitivize( tbl );
2963     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2967 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2968     NULL, /* child_added */
2969     NULL, /* child_removed */
2970     rect_tb_event_attr_changed,
2971     NULL, /* content_changed */
2972     NULL  /* order_changed */
2973 };
2975 /**
2976  *  \param selection should not be NULL.
2977  */
2978 static void
2979 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2981     int n_selected = 0;
2982     Inkscape::XML::Node *repr = NULL;
2983     SPItem *item = NULL;
2985     if ( g_object_get_data( tbl, "repr" ) ) {
2986         g_object_set_data( tbl, "item", NULL );
2987     }
2988     purge_repr_listener( tbl, tbl );
2990     for (GSList const *items = selection->itemList();
2991          items != NULL;
2992          items = items->next) {
2993         if (SP_IS_RECT((SPItem *) items->data)) {
2994             n_selected++;
2995             item = (SPItem *) items->data;
2996             repr = SP_OBJECT_REPR(item);
2997         }
2998     }
3000     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3002     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3004     if (n_selected == 0) {
3005         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3007         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3008         gtk_action_set_sensitive(w, FALSE);
3009         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3010         gtk_action_set_sensitive(h, FALSE);
3012     } else if (n_selected == 1) {
3013         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3014         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3016         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3017         gtk_action_set_sensitive(w, TRUE);
3018         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3019         gtk_action_set_sensitive(h, TRUE);
3021         if (repr) {
3022             g_object_set_data( tbl, "repr", repr );
3023             g_object_set_data( tbl, "item", item );
3024             Inkscape::GC::anchor(repr);
3025             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3026             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3027         }
3028     } else {
3029         // FIXME: implement averaging of all parameters for multiple selected
3030         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3031         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3032         sp_rtb_sensitivize( tbl );
3033     }
3037 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3039     EgeAdjustmentAction* eact = 0;
3040     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3042     {
3043         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3044         ege_output_action_set_use_markup( act, TRUE );
3045         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3046         g_object_set_data( holder, "mode_action", act );
3047     }
3049     // rx/ry units menu: create
3050     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3051     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3052     // fixme: add % meaning per cent of the width/height
3053     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3054     g_object_set_data( holder, "tracker", tracker );
3056     /* W */
3057     {
3058         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3059         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3060         eact = create_adjustment_action( "RectWidthAction",
3061                                          _("Width"), _("W:"), _("Width of rectangle"),
3062                                          "/tools/shapes/rect/width", 0,
3063                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3064                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3065                                          labels, values, G_N_ELEMENTS(labels),
3066                                          sp_rtb_width_value_changed );
3067         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3068         g_object_set_data( holder, "width_action", eact );
3069         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3070         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3071     }
3073     /* H */
3074     {
3075         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3076         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3077         eact = create_adjustment_action( "RectHeightAction",
3078                                          _("Height"), _("H:"), _("Height of rectangle"),
3079                                          "/tools/shapes/rect/height", 0,
3080                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3081                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3082                                          labels, values, G_N_ELEMENTS(labels),
3083                                          sp_rtb_height_value_changed );
3084         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3085         g_object_set_data( holder, "height_action", eact );
3086         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3087         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3088     }
3090     /* rx */
3091     {
3092         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3093         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3094         eact = create_adjustment_action( "RadiusXAction",
3095                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3096                                          "/tools/shapes/rect/rx", 0,
3097                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3098                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3099                                          labels, values, G_N_ELEMENTS(labels),
3100                                          sp_rtb_rx_value_changed);
3101         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3102         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3103     }
3105     /* ry */
3106     {
3107         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3108         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3109         eact = create_adjustment_action( "RadiusYAction",
3110                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3111                                          "/tools/shapes/rect/ry", 0,
3112                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3113                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3114                                          labels, values, G_N_ELEMENTS(labels),
3115                                          sp_rtb_ry_value_changed);
3116         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3117         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3118     }
3120     // add the units menu
3121     {
3122         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3123         gtk_action_group_add_action( mainActions, act );
3124     }
3126     /* Reset */
3127     {
3128         InkAction* inky = ink_action_new( "RectResetAction",
3129                                           _("Not rounded"),
3130                                           _("Make corners sharp"),
3131                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3132                                           secondarySize );
3133         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3134         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3135         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3136         g_object_set_data( holder, "not_rounded", inky );
3137     }
3139     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3140     sp_rtb_sensitivize( holder );
3142     sigc::connection *connection = new sigc::connection(
3143         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3144         );
3145     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3146     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3149 //########################
3150 //##       3D Box       ##
3151 //########################
3153 // normalize angle so that it lies in the interval [0,360]
3154 static double box3d_normalize_angle (double a) {
3155     double angle = a + ((int) (a/360.0))*360;
3156     if (angle < 0) {
3157         angle += 360.0;
3158     }
3159     return angle;
3162 static void
3163 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3164                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3165     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3166     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3167     //       are reset).
3168     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3170     if (is_infinite) {
3171         gtk_toggle_action_set_active(tact, TRUE);
3172         gtk_action_set_sensitive(act, TRUE);
3174         double angle = persp3d_get_infinite_angle(persp, axis);
3175         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3176             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3177         }
3178     } else {
3179         gtk_toggle_action_set_active(tact, FALSE);
3180         gtk_action_set_sensitive(act, FALSE);
3181     }
3184 static void
3185 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3186     if (!persp_repr) {
3187         g_print ("No perspective given to box3d_resync_toolbar().\n");
3188         return;
3189     }
3191     GtkWidget *tbl = GTK_WIDGET(data);
3192     GtkAdjustment *adj = 0;
3193     GtkAction *act = 0;
3194     GtkToggleAction *tact = 0;
3195     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3196     if (!persp) {
3197         // Hmm, is it an error if this happens?
3198         return;
3199     }
3200     {
3201         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3202         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3203         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3205         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3206     }
3207     {
3208         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3209         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3210         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3212         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3213     }
3214     {
3215         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3216         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3217         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3219         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3220     }
3223 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3224                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3225                                                   bool /*is_interactive*/, gpointer data)
3227     GtkWidget *tbl = GTK_WIDGET(data);
3229     // quit if run by the attr_changed or selection changed listener
3230     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3231         return;
3232     }
3234     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3235     // sp_document_maybe_done() when the document is undo insensitive)
3236     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3238     // TODO: Only update the appropriate part of the toolbar
3239 //    if (!strcmp(name, "inkscape:vp_z")) {
3240         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3241 //    }
3243     Persp3D *persp = persp3d_get_from_repr(repr);
3244     persp3d_update_box_reprs(persp);
3246     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3249 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3251     NULL, /* child_added */
3252     NULL, /* child_removed */
3253     box3d_persp_tb_event_attr_changed,
3254     NULL, /* content_changed */
3255     NULL  /* order_changed */
3256 };
3258 /**
3259  *  \param selection Should not be NULL.
3260  */
3261 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3262 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3263 static void
3264 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3266     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3267     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3268     // update the perspectives with infinite VPs and leave the other ones untouched).
3270     Inkscape::XML::Node *persp_repr = NULL;
3271     purge_repr_listener(tbl, tbl);
3273     SPItem *item = selection->singleItem();
3274     if (item && SP_IS_BOX3D(item)) {
3275         // FIXME: Also deal with multiple selected boxes
3276         SPBox3D *box = SP_BOX3D(item);
3277         Persp3D *persp = box3d_get_perspective(box);
3278         persp_repr = SP_OBJECT_REPR(persp);
3279         if (persp_repr) {
3280             g_object_set_data(tbl, "repr", persp_repr);
3281             Inkscape::GC::anchor(persp_repr);
3282             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3283             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3284         }
3286         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3287         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3288         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3290         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3291         box3d_resync_toolbar(persp_repr, tbl);
3292         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3293     }
3296 static void
3297 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3299     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3300     SPDocument *document = sp_desktop_document(desktop);
3302     // quit if run by the attr_changed or selection changed listener
3303     if (g_object_get_data( dataKludge, "freeze" )) {
3304         return;
3305     }
3307     // in turn, prevent listener from responding
3308     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3310     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3311     if (sel_persps.empty()) {
3312         // this can happen when the document is created; we silently ignore it
3313         return;
3314     }
3315     Persp3D *persp = sel_persps.front();
3317     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3318     SP_OBJECT(persp)->updateRepr();
3320     // TODO: use the correct axis here, too
3321     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3323     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3327 static void
3328 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3330     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3333 static void
3334 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3336     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3339 static void
3340 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3342     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3346 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3348     // TODO: Take all selected perspectives into account
3349     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3350     if (sel_persps.empty()) {
3351         // this can happen when the document is created; we silently ignore it
3352         return;
3353     }
3354     Persp3D *persp = sel_persps.front();
3356     bool set_infinite = gtk_toggle_action_get_active(act);
3357     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3360 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3362     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3365 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3367     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3370 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3372     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3375 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3377     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3378     EgeAdjustmentAction* eact = 0;
3379     SPDocument *document = sp_desktop_document (desktop);
3380     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3382     EgeAdjustmentAction* box3d_angle_x = 0;
3383     EgeAdjustmentAction* box3d_angle_y = 0;
3384     EgeAdjustmentAction* box3d_angle_z = 0;
3386     /* Angle X */
3387     {
3388         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3389         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3390         eact = create_adjustment_action( "3DBoxAngleXAction",
3391                                          _("Angle in X direction"), _("Angle X:"),
3392                                          // Translators: PL is short for 'perspective line'
3393                                          _("Angle of PLs in X direction"),
3394                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3395                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3396                                          -360.0, 360.0, 1.0, 10.0,
3397                                          labels, values, G_N_ELEMENTS(labels),
3398                                          box3d_angle_x_value_changed );
3399         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3400         g_object_set_data( holder, "box3d_angle_x_action", eact );
3401         box3d_angle_x = eact;
3402     }
3404     if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3405         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3406     } else {
3407         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3408     }
3411     /* VP X state */
3412     {
3413         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3414                                                       // Translators: VP is short for 'vanishing point'
3415                                                       _("State of VP in X direction"),
3416                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3417                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3418                                                       Inkscape::ICON_SIZE_DECORATION );
3419         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3420         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3421         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3422         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3423         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3424     }
3426     /* Angle Y */
3427     {
3428         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3429         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3430         eact = create_adjustment_action( "3DBoxAngleYAction",
3431                                          _("Angle in Y direction"), _("Angle Y:"),
3432                                          // Translators: PL is short for 'perspective line'
3433                                          _("Angle of PLs in Y direction"),
3434                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3435                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3436                                          -360.0, 360.0, 1.0, 10.0,
3437                                          labels, values, G_N_ELEMENTS(labels),
3438                                          box3d_angle_y_value_changed );
3439         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3440         g_object_set_data( holder, "box3d_angle_y_action", eact );
3441         box3d_angle_y = eact;
3442     }
3444     if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3445         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3446     } else {
3447         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3448     }
3450     /* VP Y state */
3451     {
3452         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3453                                                       // Translators: VP is short for 'vanishing point'
3454                                                       _("State of VP in Y direction"),
3455                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3456                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3457                                                       Inkscape::ICON_SIZE_DECORATION );
3458         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3459         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3460         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3461         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3462         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3463     }
3465     /* Angle Z */
3466     {
3467         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3468         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3469         eact = create_adjustment_action( "3DBoxAngleZAction",
3470                                          _("Angle in Z direction"), _("Angle Z:"),
3471                                          // Translators: PL is short for 'perspective line'
3472                                          _("Angle of PLs in Z direction"),
3473                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3474                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3475                                          -360.0, 360.0, 1.0, 10.0,
3476                                          labels, values, G_N_ELEMENTS(labels),
3477                                          box3d_angle_z_value_changed );
3478         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3479         g_object_set_data( holder, "box3d_angle_z_action", eact );
3480         box3d_angle_z = eact;
3481     }
3483     if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3484         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3485     } else {
3486         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3487     }
3489     /* VP Z state */
3490     {
3491         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3492                                                       // Translators: VP is short for 'vanishing point'
3493                                                       _("State of VP in Z direction"),
3494                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3495                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3496                                                       Inkscape::ICON_SIZE_DECORATION );
3497         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3498         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3499         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3500         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3501         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3502     }
3504     sigc::connection *connection = new sigc::connection(
3505         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3506        );
3507     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3508     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3511 //########################
3512 //##       Spiral       ##
3513 //########################
3515 static void
3516 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3518     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3520     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3521         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3522         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3523     }
3525     // quit if run by the attr_changed listener
3526     if (g_object_get_data( tbl, "freeze" )) {
3527         return;
3528     }
3530     // in turn, prevent listener from responding
3531     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3533     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3535     bool modmade = false;
3536     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3537          items != NULL;
3538          items = items->next)
3539     {
3540         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3541             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3542             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3543             SP_OBJECT((SPItem *) items->data)->updateRepr();
3544             modmade = true;
3545         }
3546     }
3548     g_free(namespaced_name);
3550     if (modmade) {
3551         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3552                                    _("Change spiral"));
3553     }
3555     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3558 static void
3559 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3561     sp_spl_tb_value_changed(adj, tbl, "revolution");
3564 static void
3565 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3567     sp_spl_tb_value_changed(adj, tbl, "expansion");
3570 static void
3571 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3573     sp_spl_tb_value_changed(adj, tbl, "t0");
3576 static void
3577 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3579     GtkWidget *tbl = GTK_WIDGET(obj);
3581     GtkAdjustment *adj;
3583     // fixme: make settable
3584     gdouble rev = 5;
3585     gdouble exp = 1.0;
3586     gdouble t0 = 0.0;
3588     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3589     gtk_adjustment_set_value(adj, rev);
3590     gtk_adjustment_value_changed(adj);
3592     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3593     gtk_adjustment_set_value(adj, exp);
3594     gtk_adjustment_value_changed(adj);
3596     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3597     gtk_adjustment_set_value(adj, t0);
3598     gtk_adjustment_value_changed(adj);
3600     spinbutton_defocus(GTK_OBJECT(tbl));
3604 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3605                                          gchar const */*old_value*/, gchar const */*new_value*/,
3606                                          bool /*is_interactive*/, gpointer data)
3608     GtkWidget *tbl = GTK_WIDGET(data);
3610     // quit if run by the _changed callbacks
3611     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3612         return;
3613     }
3615     // in turn, prevent callbacks from responding
3616     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3618     GtkAdjustment *adj;
3619     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3620     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3622     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3623     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3625     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3626     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3628     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3632 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3633     NULL, /* child_added */
3634     NULL, /* child_removed */
3635     spiral_tb_event_attr_changed,
3636     NULL, /* content_changed */
3637     NULL  /* order_changed */
3638 };
3640 static void
3641 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3643     int n_selected = 0;
3644     Inkscape::XML::Node *repr = NULL;
3646     purge_repr_listener( tbl, tbl );
3648     for (GSList const *items = selection->itemList();
3649          items != NULL;
3650          items = items->next)
3651     {
3652         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3653             n_selected++;
3654             repr = SP_OBJECT_REPR((SPItem *) items->data);
3655         }
3656     }
3658     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3660     if (n_selected == 0) {
3661         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3662     } else if (n_selected == 1) {
3663         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3665         if (repr) {
3666             g_object_set_data( tbl, "repr", repr );
3667             Inkscape::GC::anchor(repr);
3668             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3669             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3670         }
3671     } else {
3672         // FIXME: implement averaging of all parameters for multiple selected
3673         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3674         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3675     }
3679 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3681     EgeAdjustmentAction* eact = 0;
3682     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3684     {
3685         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3686         ege_output_action_set_use_markup( act, TRUE );
3687         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3688         g_object_set_data( holder, "mode_action", act );
3689     }
3691     /* Revolution */
3692     {
3693         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3694         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3695         eact = create_adjustment_action( "SpiralRevolutionAction",
3696                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3697                                          "/tools/shapes/spiral/revolution", 3.0,
3698                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3699                                          0.01, 1024.0, 0.1, 1.0,
3700                                          labels, values, G_N_ELEMENTS(labels),
3701                                          sp_spl_tb_revolution_value_changed, 1, 2);
3702         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3703     }
3705     /* Expansion */
3706     {
3707         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3708         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3709         eact = create_adjustment_action( "SpiralExpansionAction",
3710                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3711                                          "/tools/shapes/spiral/expansion", 1.0,
3712                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3713                                          0.0, 1000.0, 0.01, 1.0,
3714                                          labels, values, G_N_ELEMENTS(labels),
3715                                          sp_spl_tb_expansion_value_changed);
3716         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3717     }
3719     /* T0 */
3720     {
3721         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3722         gdouble values[] = {0, 0.5, 0.9};
3723         eact = create_adjustment_action( "SpiralT0Action",
3724                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3725                                          "/tools/shapes/spiral/t0", 0.0,
3726                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3727                                          0.0, 0.999, 0.01, 1.0,
3728                                          labels, values, G_N_ELEMENTS(labels),
3729                                          sp_spl_tb_t0_value_changed);
3730         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3731     }
3733     /* Reset */
3734     {
3735         InkAction* inky = ink_action_new( "SpiralResetAction",
3736                                           _("Defaults"),
3737                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3738                                           GTK_STOCK_CLEAR,
3739                                           secondarySize );
3740         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3741         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3742     }
3745     sigc::connection *connection = new sigc::connection(
3746         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3747         );
3748     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3749     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3752 //########################
3753 //##     Pen/Pencil     ##
3754 //########################
3756 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3757 static Glib::ustring const
3758 freehand_tool_name(GObject *dataKludge)
3760     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3761     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3762              ? "/tools/freehand/pen"
3763              : "/tools/freehand/pencil" );
3766 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3768     gint mode = ege_select_one_action_get_active(act);
3770     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3771     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3773     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3775     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3776     // preparatory work here
3777     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3778         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3779         sp_pen_context_set_polyline_mode(pc);
3780     }
3783 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3785     /* Freehand mode toggle buttons */
3786     {
3787         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3788         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3789         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3791         {
3792             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3794             GtkTreeIter iter;
3795             gtk_list_store_append( model, &iter );
3796             gtk_list_store_set( model, &iter,
3797                                 0, _("Bezier"),
3798                                 1, _("Create regular Bezier path"),
3799                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3800                                 -1 );
3802             gtk_list_store_append( model, &iter );
3803             gtk_list_store_set( model, &iter,
3804                                 0, _("Spiro"),
3805                                 1, _("Create Spiro path"),
3806                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3807                                 -1 );
3809             if (!tool_is_pencil) {
3810                 gtk_list_store_append( model, &iter );
3811                 gtk_list_store_set( model, &iter,
3812                                     0, _("Zigzag"),
3813                                     1, _("Create a sequence of straight line segments"),
3814                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3815                                     -1 );
3817                 gtk_list_store_append( model, &iter );
3818                 gtk_list_store_set( model, &iter,
3819                                     0, _("Paraxial"),
3820                                     1, _("Create a sequence of paraxial line segments"),
3821                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3822                                     -1 );
3823             }
3825             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3826                                                                 "FreehandModeActionPencil" :
3827                                                                 "FreehandModeActionPen",
3828                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3829             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3831             ege_select_one_action_set_appearance( act, "full" );
3832             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3833             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3834             ege_select_one_action_set_icon_column( act, 2 );
3835             ege_select_one_action_set_icon_size( act, secondarySize );
3836             ege_select_one_action_set_tooltip_column( act, 1  );
3838             ege_select_one_action_set_active( act, freehandMode);
3839             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3840         }
3841     }
3844 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3845     gint shape = ege_select_one_action_get_active( act );
3846     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3847     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3850 /**
3851  * \brief Generate the list of freehand advanced shape option entries.
3852  */
3853 GList * freehand_shape_dropdown_items_list() {
3854     GList *glist = NULL;
3856     glist = g_list_append (glist, _("None"));
3857     glist = g_list_append (glist, _("Triangle in"));
3858     glist = g_list_append (glist, _("Triangle out"));
3859     glist = g_list_append (glist, _("Ellipse"));
3860     glist = g_list_append (glist, _("From clipboard"));
3862     return glist;
3865 static void
3866 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3867     /*advanced shape options */
3868     {
3869         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3870         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3872         GList* items = 0;
3873         gint count = 0;
3874         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3875         {
3876             GtkTreeIter iter;
3877             gtk_list_store_append( model, &iter );
3878             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3879             count++;
3880         }
3881         g_list_free( items );
3882         items = 0;
3883         EgeSelectOneAction* act1 = ege_select_one_action_new(
3884             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3885             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3886         g_object_set( act1, "short_label", _("Shape:"), NULL );
3887         ege_select_one_action_set_appearance( act1, "compact" );
3888         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3889         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3890         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3891         g_object_set_data( holder, "shape_action", act1 );
3892     }
3895 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3897     sp_add_freehand_mode_toggle(mainActions, holder, false);
3898     freehand_add_advanced_shape_options(mainActions, holder, false);
3902 static void
3903 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3905     GtkWidget *tbl = GTK_WIDGET(obj);
3907     GtkAdjustment *adj;
3909     // fixme: make settable
3910     gdouble tolerance = 4;
3912     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3913     gtk_adjustment_set_value(adj, tolerance);
3914     gtk_adjustment_value_changed(adj);
3916     spinbutton_defocus(GTK_OBJECT(tbl));
3919 static void
3920 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3922     // quit if run by the attr_changed listener
3923     if (g_object_get_data( tbl, "freeze" )) {
3924         return;
3925     }
3926     // in turn, prevent listener from responding
3927     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3928     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3929     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3930     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3933 /*
3934 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3935 public:
3936     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3937     {
3938         g_object_set_data(_obj, "prefobserver", this);
3939     }
3940     virtual ~PencilToleranceObserver() {
3941         if (g_object_get_data(_obj, "prefobserver") == this) {
3942             g_object_set_data(_obj, "prefobserver", NULL);
3943         }
3944     }
3945     virtual void notify(Inkscape::Preferences::Entry const &val) {
3946         GObject* tbl = _obj;
3947         if (g_object_get_data( tbl, "freeze" )) {
3948             return;
3949         }
3950         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3952         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3954         double v = val.getDouble(adj->value);
3955         gtk_adjustment_set_value(adj, v);
3956         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3957     }
3958 private:
3959     GObject *_obj;
3960 };
3961 */
3963 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3965     sp_add_freehand_mode_toggle(mainActions, holder, true);
3967     EgeAdjustmentAction* eact = 0;
3969     /* Tolerance */
3970     {
3971         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3972         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3973         eact = create_adjustment_action( "PencilToleranceAction",
3974                                          _("Smoothing:"), _("Smoothing: "),
3975                  _("How much smoothing (simplifying) is applied to the line"),
3976                                          "/tools/freehand/pencil/tolerance",
3977                                          3.0,
3978                                          GTK_WIDGET(desktop->canvas), NULL,
3979                                          holder, TRUE, "altx-pencil",
3980                                          1, 100.0, 0.5, 1.0,
3981                                          labels, values, G_N_ELEMENTS(labels),
3982                                          sp_pencil_tb_tolerance_value_changed,
3983                                          1, 2);
3984         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3985         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3986     }
3988     /* advanced shape options */
3989     freehand_add_advanced_shape_options(mainActions, holder, true);
3991     /* Reset */
3992     {
3993         InkAction* inky = ink_action_new( "PencilResetAction",
3994                                           _("Defaults"),
3995                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3996                                           GTK_STOCK_CLEAR,
3997                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3998         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3999         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4000     }
4002     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4007 //########################
4008 //##       Tweak        ##
4009 //########################
4011 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4013     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4014     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4017 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4019     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4020     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4023 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4025     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4026     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4029 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4031     int mode = ege_select_one_action_get_active( act );
4032     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4033     prefs->setInt("/tools/tweak/mode", mode);
4035     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4036     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4037     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4038     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4039     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4040     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4041     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4042         if (doh) gtk_action_set_sensitive (doh, TRUE);
4043         if (dos) gtk_action_set_sensitive (dos, TRUE);
4044         if (dol) gtk_action_set_sensitive (dol, TRUE);
4045         if (doo) gtk_action_set_sensitive (doo, TRUE);
4046         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4047         if (fid) gtk_action_set_sensitive (fid, FALSE);
4048     } else {
4049         if (doh) gtk_action_set_sensitive (doh, FALSE);
4050         if (dos) gtk_action_set_sensitive (dos, FALSE);
4051         if (dol) gtk_action_set_sensitive (dol, FALSE);
4052         if (doo) gtk_action_set_sensitive (doo, FALSE);
4053         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4054         if (fid) gtk_action_set_sensitive (fid, TRUE);
4055     }
4058 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4060     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4061     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4064 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4065     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4066     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4068 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4069     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4070     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4072 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4073     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4076 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4077     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4078     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4081 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4083     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4084     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4086     {
4087         /* Width */
4088         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4089         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4090         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4091                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4092                                                               "/tools/tweak/width", 15,
4093                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4094                                                               1, 100, 1.0, 10.0,
4095                                                               labels, values, G_N_ELEMENTS(labels),
4096                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4097         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4098         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4099         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4100     }
4103     {
4104         /* Force */
4105         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4106         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4107         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4108                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4109                                                               "/tools/tweak/force", 20,
4110                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4111                                                               1, 100, 1.0, 10.0,
4112                                                               labels, values, G_N_ELEMENTS(labels),
4113                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4114         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4115         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4116         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4117     }
4119     /* Mode */
4120     {
4121         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4123         GtkTreeIter iter;
4124         gtk_list_store_append( model, &iter );
4125         gtk_list_store_set( model, &iter,
4126                             0, _("Move mode"),
4127                             1, _("Move objects in any direction"),
4128                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4129                             -1 );
4131         gtk_list_store_append( model, &iter );
4132         gtk_list_store_set( model, &iter,
4133                             0, _("Move in/out mode"),
4134                             1, _("Move objects towards cursor; with Shift from cursor"),
4135                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4136                             -1 );
4138         gtk_list_store_append( model, &iter );
4139         gtk_list_store_set( model, &iter,
4140                             0, _("Move jitter mode"),
4141                             1, _("Move objects in random directions"),
4142                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4143                             -1 );
4145         gtk_list_store_append( model, &iter );
4146         gtk_list_store_set( model, &iter,
4147                             0, _("Scale mode"),
4148                             1, _("Shrink objects, with Shift enlarge"),
4149                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4150                             -1 );
4152         gtk_list_store_append( model, &iter );
4153         gtk_list_store_set( model, &iter,
4154                             0, _("Rotate mode"),
4155                             1, _("Rotate objects, with Shift counterclockwise"),
4156                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4157                             -1 );
4159         gtk_list_store_append( model, &iter );
4160         gtk_list_store_set( model, &iter,
4161                             0, _("Duplicate/delete mode"),
4162                             1, _("Duplicate objects, with Shift delete"),
4163                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4164                             -1 );
4166         gtk_list_store_append( model, &iter );
4167         gtk_list_store_set( model, &iter,
4168                             0, _("Push mode"),
4169                             1, _("Push parts of paths in any direction"),
4170                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4171                             -1 );
4173         gtk_list_store_append( model, &iter );
4174         gtk_list_store_set( model, &iter,
4175                             0, _("Shrink/grow mode"),
4176                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4177                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4178                             -1 );
4180         gtk_list_store_append( model, &iter );
4181         gtk_list_store_set( model, &iter,
4182                             0, _("Attract/repel mode"),
4183                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4184                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4185                             -1 );
4187         gtk_list_store_append( model, &iter );
4188         gtk_list_store_set( model, &iter,
4189                             0, _("Roughen mode"),
4190                             1, _("Roughen parts of paths"),
4191                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4192                             -1 );
4194         gtk_list_store_append( model, &iter );
4195         gtk_list_store_set( model, &iter,
4196                             0, _("Color paint mode"),
4197                             1, _("Paint the tool's color upon selected objects"),
4198                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4199                             -1 );
4201         gtk_list_store_append( model, &iter );
4202         gtk_list_store_set( model, &iter,
4203                             0, _("Color jitter mode"),
4204                             1, _("Jitter the colors of selected objects"),
4205                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4206                             -1 );
4208         gtk_list_store_append( model, &iter );
4209         gtk_list_store_set( model, &iter,
4210                             0, _("Blur mode"),
4211                             1, _("Blur selected objects more; with Shift, blur less"),
4212                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4213                             -1 );
4216         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4217         g_object_set( act, "short_label", _("Mode:"), NULL );
4218         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4219         g_object_set_data( holder, "mode_action", act );
4221         ege_select_one_action_set_appearance( act, "full" );
4222         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4223         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4224         ege_select_one_action_set_icon_column( act, 2 );
4225         ege_select_one_action_set_icon_size( act, secondarySize );
4226         ege_select_one_action_set_tooltip_column( act, 1  );
4228         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4229         ege_select_one_action_set_active( act, mode );
4230         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4232         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4233     }
4235     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4237     {
4238         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4239         ege_output_action_set_use_markup( act, TRUE );
4240         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4241         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4242             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4243         g_object_set_data( holder, "tweak_channels_label", act);
4244     }
4246     {
4247         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4248                                                       _("Hue"),
4249                                                       _("In color mode, act on objects' hue"),
4250                                                       NULL,
4251                                                       Inkscape::ICON_SIZE_DECORATION );
4252         //TRANSLATORS:  "H" here stands for hue
4253         g_object_set( act, "short_label", _("H"), NULL );
4254         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4255         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4256         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4257         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4258             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4259         g_object_set_data( holder, "tweak_doh", act);
4260     }
4261     {
4262         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4263                                                       _("Saturation"),
4264                                                       _("In color mode, act on objects' saturation"),
4265                                                       NULL,
4266                                                       Inkscape::ICON_SIZE_DECORATION );
4267         //TRANSLATORS: "S" here stands for Saturation
4268         g_object_set( act, "short_label", _("S"), NULL );
4269         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4270         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4271         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4272         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4273             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4274         g_object_set_data( holder, "tweak_dos", act );
4275     }
4276     {
4277         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4278                                                       _("Lightness"),
4279                                                       _("In color mode, act on objects' lightness"),
4280                                                       NULL,
4281                                                       Inkscape::ICON_SIZE_DECORATION );
4282         //TRANSLATORS: "L" here stands for Lightness
4283         g_object_set( act, "short_label", _("L"), NULL );
4284         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4285         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4286         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4287         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4288             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4289         g_object_set_data( holder, "tweak_dol", act );
4290     }
4291     {
4292         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4293                                                       _("Opacity"),
4294                                                       _("In color mode, act on objects' opacity"),
4295                                                       NULL,
4296                                                       Inkscape::ICON_SIZE_DECORATION );
4297         //TRANSLATORS: "O" here stands for Opacity
4298         g_object_set( act, "short_label", _("O"), NULL );
4299         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4300         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4301         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4302         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4303             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4304         g_object_set_data( holder, "tweak_doo", act );
4305     }
4307     {   /* Fidelity */
4308         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4309         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4310         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4311                                                               _("Fidelity"), _("Fidelity:"),
4312                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4313                                                               "/tools/tweak/fidelity", 50,
4314                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4315                                                               1, 100, 1.0, 10.0,
4316                                                               labels, values, G_N_ELEMENTS(labels),
4317                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4318         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4319         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4320         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4321             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4322         g_object_set_data( holder, "tweak_fidelity", eact );
4323     }
4326     /* Use Pressure button */
4327     {
4328         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4329                                                       _("Pressure"),
4330                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4331                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4332                                                       Inkscape::ICON_SIZE_DECORATION );
4333         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4334         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4335         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4336     }
4341 //########################
4342 //##       Spray        ##
4343 //########################
4345 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4347     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4348     prefs->setDouble( "/tools/spray/width", adj->value );
4351 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4353     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4354     prefs->setDouble( "/tools/spray/mean", adj->value );
4357 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4359     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4360     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4363 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4365     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4366     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4369 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4371     int mode = ege_select_one_action_get_active( act );
4372     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4373     prefs->setInt("/tools/spray/mode", mode);
4376 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4378     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4379     prefs->setDouble( "/tools/spray/population", adj->value );
4382 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4384     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4385     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4388 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4390     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4391     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4395 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4397     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4398     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4400     {
4401         /* Width */
4402         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4403         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4404         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4405                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4406                                                               "/tools/spray/width", 15,
4407                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4408                                                               1, 100, 1.0, 10.0,
4409                                                               labels, values, G_N_ELEMENTS(labels),
4410                                                               sp_spray_width_value_changed,  1, 0 );
4411         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4412         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4413         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4414     }
4416     {
4417         /* Mean */
4418         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4419         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4420         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4421                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4422                                                               "/tools/spray/mean", 0,
4423                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4424                                                               0, 100, 1.0, 10.0,
4425                                                               labels, values, G_N_ELEMENTS(labels),
4426                                                               sp_spray_mean_value_changed,  1, 0 );
4427         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4428         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4429         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4430     }
4432     {
4433         /* Standard_deviation */
4434         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4435         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4436         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4437                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4438                                                               "/tools/spray/standard_deviation", 70,
4439                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4440                                                               1, 100, 1.0, 10.0,
4441                                                               labels, values, G_N_ELEMENTS(labels),
4442                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4443         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4444         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4445         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4446     }
4448     /* Mode */
4449     {
4450         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4452         GtkTreeIter iter;
4453         gtk_list_store_append( model, &iter );
4454         gtk_list_store_set( model, &iter,
4455                             0, _("Spray with copies"),
4456                             1, _("Spray copies of the initial selection"),
4457                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4458                             -1 );
4460         gtk_list_store_append( model, &iter );
4461         gtk_list_store_set( model, &iter,
4462                             0, _("Spray with clones"),
4463                             1, _("Spray clones of the initial selection"),
4464                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4465                             -1 );
4467         gtk_list_store_append( model, &iter );
4468         gtk_list_store_set( model, &iter,
4469                             0, _("Spray single path"),
4470                             1, _("Spray objects in a single path"),
4471                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4472                             -1 );
4474         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4475         g_object_set( act, "short_label", _("Mode:"), NULL );
4476         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4477         g_object_set_data( holder, "mode_action", act );
4479         ege_select_one_action_set_appearance( act, "full" );
4480         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4481         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4482         ege_select_one_action_set_icon_column( act, 2 );
4483         ege_select_one_action_set_icon_size( act, secondarySize );
4484         ege_select_one_action_set_tooltip_column( act, 1  );
4486         gint mode = prefs->getInt("/tools/spray/mode", 1);
4487         ege_select_one_action_set_active( act, mode );
4488         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4490         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4491     }
4493     {   /* Population */
4494         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4495         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4496         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4497                                                               _("Amount"), _("Amount:"),
4498                                                               _("Adjusts the number of items sprayed per clic."),
4499                                                               "/tools/spray/population", 70,
4500                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4501                                                               1, 100, 1.0, 10.0,
4502                                                               labels, values, G_N_ELEMENTS(labels),
4503                                                               sp_spray_population_value_changed,  1, 0 );
4504         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4505         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4506         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4507         g_object_set_data( holder, "spray_population", eact );
4508     }
4510     /* Use Pressure button */
4511     {
4512         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4513                                                       _("Pressure"),
4514                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4515                                                       "use_pressure",
4516                                                       Inkscape::ICON_SIZE_DECORATION );
4517         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4518         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4519         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4520     }
4522     {   /* Rotation */
4523         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4524         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4525         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4526                                                               _("Rotation"), _("Rotation:"),
4527                                                               // xgettext:no-c-format
4528                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4529                                                               "/tools/spray/rotation_variation", 0,
4530                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4531                                                               0, 100, 1.0, 10.0,
4532                                                               labels, values, G_N_ELEMENTS(labels),
4533                                                               sp_spray_rotation_value_changed,  1, 0 );
4534         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4535         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4536         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4537         g_object_set_data( holder, "spray_rotation", eact );
4538     }
4540     {   /* Scale */
4541         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4542         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4543         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4544                                                               _("Scale"), _("Scale:"),
4545                                                               // xgettext:no-c-format
4546                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4547                                                               "/tools/spray/scale_variation", 0,
4548                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4549                                                               0, 100, 1.0, 10.0,
4550                                                               labels, values, G_N_ELEMENTS(labels),
4551                                                               sp_spray_scale_value_changed,  1, 0 );
4552         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4553         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4554         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4555         g_object_set_data( holder, "spray_scale", eact );
4556     }
4563 //########################
4564 //##     Calligraphy    ##
4565 //########################
4566 static void update_presets_list (GObject *tbl)
4568     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4569     if (g_object_get_data(tbl, "presets_blocked"))
4570         return;
4572     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4573     if (!sel) {
4574         // WTF!? This will cause a segfault if ever reached
4575         //ege_select_one_action_set_active(sel, 0);
4576         return;
4577     }
4579     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4581     int ege_index = 1;
4582     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4583         bool match = true;
4585         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4586         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4587             Glib::ustring entry_name = j->getEntryName();
4588             if (entry_name == "id" || entry_name == "name") continue;
4590             void *widget = g_object_get_data(tbl, entry_name.data());
4591             if (widget) {
4592                 if (GTK_IS_ADJUSTMENT(widget)) {
4593                     double v = j->getDouble();
4594                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4595                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4596                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4597                         match = false;
4598                         break;
4599                     }
4600                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4601                     bool v = j->getBool();
4602                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4603                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4604                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4605                         match = false;
4606                         break;
4607                     }
4608                 }
4609             }
4610         }
4612         if (match) {
4613             // newly added item is at the same index as the
4614             // save command, so we need to change twice for it to take effect
4615             ege_select_one_action_set_active(sel, 0);
4616             ege_select_one_action_set_active(sel, ege_index); // one-based index
4617             return;
4618         }
4619     }
4621     // no match found
4622     ege_select_one_action_set_active(sel, 0);
4625 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4627     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4628     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4629     update_presets_list(tbl);
4632 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4634     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4635     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4636     update_presets_list(tbl);
4639 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4641     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4642     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4643     update_presets_list(tbl);
4646 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4648     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4649     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4650     update_presets_list(tbl);
4653 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4655     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4656     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4657     update_presets_list(tbl);
4660 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4662     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4663     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4664     update_presets_list(tbl);
4667 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4669     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4670     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4671     update_presets_list(tbl);
4674 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4676     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4677     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4678     update_presets_list(tbl);
4681 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4683     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4684     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4685     update_presets_list(tbl);
4688 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4690     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4691     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4692     update_presets_list(tbl);
4695 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4697     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4698     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4699     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4700     update_presets_list(tbl);
4701     if (calligraphy_angle )
4702         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4706 static gchar const *const widget_names[] = {
4707     "width",
4708     "mass",
4709     "wiggle",
4710     "angle",
4711     "thinning",
4712     "tremor",
4713     "flatness",
4714     "cap_rounding",
4715     "usepressure",
4716     "tracebackground",
4717     "usetilt"
4718 };
4721 static void sp_dcc_build_presets_list(GObject *tbl)
4723     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4725     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4726     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4727     gtk_list_store_clear (model);
4729     {
4730         GtkTreeIter iter;
4731         gtk_list_store_append( model, &iter );
4732         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4733     }
4735     // iterate over all presets to populate the list
4736     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4737     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4738     int ii=1;
4740     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4741         GtkTreeIter iter;
4742         Glib::ustring preset_name = prefs->getString(*i + "/name");
4743         gtk_list_store_append( model, &iter );
4744         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4745     }
4747     {
4748         GtkTreeIter iter;
4749         gtk_list_store_append( model, &iter );
4750         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4751         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4752     }
4754     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4756     update_presets_list (tbl);
4759 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4761     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4762     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4763     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4764     if (! desktop) return;
4766     if (g_object_get_data(tbl, "presets_blocked"))
4767         return;
4769     CalligraphicProfileRename::show(desktop);
4770     if ( !CalligraphicProfileRename::applied()) {
4771         // dialog cancelled
4772         update_presets_list (tbl);
4773         return;
4774     }
4775     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4777     if (profile_name.empty()) {
4778         // empty name entered
4779         update_presets_list (tbl);
4780         return;
4781     }
4783     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4785     // If there's a preset with the given name, find it and set save_path appropriately
4786     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4787     int total_presets = presets.size();
4788     int new_index = -1;
4789     Glib::ustring save_path; // profile pref path without a trailing slash
4791     int temp_index = 0;
4792     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4793         Glib::ustring name = prefs->getString(*i + "/name");
4794         if (!name.empty() && profile_name == name) {
4795             new_index = temp_index;
4796             save_path = *i;
4797             break;
4798         }
4799     }
4801     if (new_index == -1) {
4802         // no preset with this name, create
4803         new_index = total_presets + 1;
4804         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4805         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4806         g_free(profile_id);
4807     }
4809     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4810         gchar const *const widget_name = widget_names[i];
4811         void *widget = g_object_get_data(tbl, widget_name);
4812         if (widget) {
4813             if (GTK_IS_ADJUSTMENT(widget)) {
4814                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4815                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4816                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4817             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4818                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4819                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4820                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4821             } else {
4822                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4823             }
4824         } else {
4825             g_warning("Bad key when writing preset: %s\n", widget_name);
4826         }
4827     }
4828     prefs->setString(save_path + "/name", profile_name);
4830     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4831     sp_dcc_build_presets_list (tbl);
4835 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4837     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4839     gint preset_index = ege_select_one_action_get_active( act );
4840     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4841     // even when the preset is not changed. It would be good to replace it with something more
4842     // modern. Index 0 means "No preset", so we don't do anything.
4843     if (preset_index == 0) return;
4845     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4847     if (preset_index == save_presets_index) {
4848         // this is the Save command
4849         sp_dcc_save_profile(NULL, tbl);
4850         return;
4851     }
4853     if (g_object_get_data(tbl, "presets_blocked"))
4854         return;
4856     // preset_index is one-based so we subtract 1
4857     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4858     Glib::ustring preset_path = presets.at(preset_index - 1);
4860     if (!preset_path.empty()) {
4861         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
4863         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4865         // Shouldn't this be std::map?
4866         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4867             Glib::ustring entry_name = i->getEntryName();
4868             if (entry_name == "id" || entry_name == "name") continue;
4869             void *widget = g_object_get_data(tbl, entry_name.data());
4870             if (widget) {
4871                 if (GTK_IS_ADJUSTMENT(widget)) {
4872                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4873                     gtk_adjustment_set_value(adj, i->getDouble());
4874                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4875                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4876                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4877                     gtk_toggle_action_set_active(toggle, i->getBool());
4878                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4879                 } else {
4880                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4881                 }
4882             } else {
4883                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4884             }
4885         }
4886         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4887     }
4891 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4893     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4894     {
4895         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4897         EgeAdjustmentAction* calligraphy_angle = 0;
4899         {
4900         /* Width */
4901         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4902         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4903         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4904                                                               _("Pen Width"), _("Width:"),
4905                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4906                                                               "/tools/calligraphic/width", 15,
4907                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4908                                                               1, 100, 1.0, 10.0,
4909                                                               labels, values, G_N_ELEMENTS(labels),
4910                                                               sp_ddc_width_value_changed,  1, 0 );
4911         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4912         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4913         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4914         }
4916         {
4917         /* Thinning */
4918             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4919             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4920         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4921                                                               _("Stroke Thinning"), _("Thinning:"),
4922                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4923                                                               "/tools/calligraphic/thinning", 10,
4924                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4925                                                               -100, 100, 1, 10.0,
4926                                                               labels, values, G_N_ELEMENTS(labels),
4927                                                               sp_ddc_velthin_value_changed, 1, 0);
4928         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4929         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4930         }
4932         {
4933         /* Angle */
4934         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4935         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4936         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4937                                                               _("Pen Angle"), _("Angle:"),
4938                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4939                                                               "/tools/calligraphic/angle", 30,
4940                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4941                                                               -90.0, 90.0, 1.0, 10.0,
4942                                                               labels, values, G_N_ELEMENTS(labels),
4943                                                               sp_ddc_angle_value_changed, 1, 0 );
4944         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4945         g_object_set_data( holder, "angle_action", eact );
4946         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4947         calligraphy_angle = eact;
4948         }
4950         {
4951         /* Fixation */
4952             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4953         gdouble values[] = {0, 20, 40, 60, 90, 100};
4954         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4955                                                               _("Fixation"), _("Fixation:"),
4956                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4957                                                               "/tools/calligraphic/flatness", 90,
4958                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4959                                                               0.0, 100, 1.0, 10.0,
4960                                                               labels, values, G_N_ELEMENTS(labels),
4961                                                               sp_ddc_flatness_value_changed, 1, 0);
4962         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4963         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4964         }
4966         {
4967         /* Cap Rounding */
4968             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4969         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4970         // TRANSLATORS: "cap" means "end" (both start and finish) here
4971         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4972                                                               _("Cap rounding"), _("Caps:"),
4973                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4974                                                               "/tools/calligraphic/cap_rounding", 0.0,
4975                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4976                                                               0.0, 5.0, 0.01, 0.1,
4977                                                               labels, values, G_N_ELEMENTS(labels),
4978                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4979         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4980         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4981         }
4983         {
4984         /* Tremor */
4985             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4986         gdouble values[] = {0, 10, 20, 40, 60, 100};
4987         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4988                                                               _("Stroke Tremor"), _("Tremor:"),
4989                                                               _("Increase to make strokes rugged and trembling"),
4990                                                               "/tools/calligraphic/tremor", 0.0,
4991                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4992                                                               0.0, 100, 1, 10.0,
4993                                                               labels, values, G_N_ELEMENTS(labels),
4994                                                               sp_ddc_tremor_value_changed, 1, 0);
4996         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4997         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4998         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4999         }
5001         {
5002         /* Wiggle */
5003         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5004         gdouble values[] = {0, 20, 40, 60, 100};
5005         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5006                                                               _("Pen Wiggle"), _("Wiggle:"),
5007                                                               _("Increase to make the pen waver and wiggle"),
5008                                                               "/tools/calligraphic/wiggle", 0.0,
5009                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5010                                                               0.0, 100, 1, 10.0,
5011                                                               labels, values, G_N_ELEMENTS(labels),
5012                                                               sp_ddc_wiggle_value_changed, 1, 0);
5013         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5014         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5015         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5016         }
5018         {
5019         /* Mass */
5020             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5021         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5022         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5023                                                               _("Pen Mass"), _("Mass:"),
5024                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5025                                                               "/tools/calligraphic/mass", 2.0,
5026                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5027                                                               0.0, 100, 1, 10.0,
5028                                                               labels, values, G_N_ELEMENTS(labels),
5029                                                               sp_ddc_mass_value_changed, 1, 0);
5030         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5031         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5032         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5033         }
5036         /* Trace Background button */
5037         {
5038             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5039                                                           _("Trace Background"),
5040                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5041                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5042                                                           Inkscape::ICON_SIZE_DECORATION );
5043             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5044             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5045             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5046             g_object_set_data( holder, "tracebackground", act );
5047         }
5049         /* Use Pressure button */
5050         {
5051             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5052                                                           _("Pressure"),
5053                                                           _("Use the pressure of the input device to alter the width of the pen"),
5054                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5055                                                           Inkscape::ICON_SIZE_DECORATION );
5056             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5057             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5058             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5059             g_object_set_data( holder, "usepressure", act );
5060         }
5062         /* Use Tilt button */
5063         {
5064             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5065                                                           _("Tilt"),
5066                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5067                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5068                                                           Inkscape::ICON_SIZE_DECORATION );
5069             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5070             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5071             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5072             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5073             g_object_set_data( holder, "usetilt", act );
5074         }
5076         /*calligraphic profile */
5077         {
5078             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5079             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5080             ege_select_one_action_set_appearance (act1, "compact");
5081             g_object_set_data (holder, "profile_selector", act1 );
5083             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5085             sp_dcc_build_presets_list (holder);
5087             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5088             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5089         }
5090     }
5094 //########################
5095 //##    Circle / Arc    ##
5096 //########################
5098 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5100     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5101     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5103     if (v1 == 0 && v2 == 0) {
5104         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5105             gtk_action_set_sensitive( ocb, FALSE );
5106             gtk_action_set_sensitive( make_whole, FALSE );
5107         }
5108     } else {
5109         gtk_action_set_sensitive( ocb, TRUE );
5110         gtk_action_set_sensitive( make_whole, TRUE );
5111     }
5114 static void
5115 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5117     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5119     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5120         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5121         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5122     }
5124     // quit if run by the attr_changed listener
5125     if (g_object_get_data( tbl, "freeze" )) {
5126         return;
5127     }
5129     // in turn, prevent listener from responding
5130     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5132     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5134     bool modmade = false;
5135     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5136          items != NULL;
5137          items = items->next)
5138     {
5139         SPItem *item = SP_ITEM(items->data);
5141         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5143             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5144             SPArc *arc = SP_ARC(item);
5146             if (!strcmp(value_name, "start"))
5147                 ge->start = (adj->value * M_PI)/ 180;
5148             else
5149                 ge->end = (adj->value * M_PI)/ 180;
5151             sp_genericellipse_normalize(ge);
5152             ((SPObject *)arc)->updateRepr();
5153             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5155             modmade = true;
5156         }
5157     }
5159     g_free(namespaced_name);
5161     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5163     sp_arctb_sensitivize( tbl, adj->value, other->value );
5165     if (modmade) {
5166         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5167                                    _("Arc: Change start/end"));
5168     }
5170     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5174 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5176     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5179 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5181     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5185 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5187     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5188     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5189         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5190         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5191     }
5193     // quit if run by the attr_changed listener
5194     if (g_object_get_data( tbl, "freeze" )) {
5195         return;
5196     }
5198     // in turn, prevent listener from responding
5199     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5201     bool modmade = false;
5203     if ( ege_select_one_action_get_active(act) != 0 ) {
5204         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5205              items != NULL;
5206              items = items->next)
5207         {
5208             if (SP_IS_ARC((SPItem *) items->data)) {
5209                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5210                 repr->setAttribute("sodipodi:open", "true");
5211                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5212                 modmade = true;
5213             }
5214         }
5215     } else {
5216         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5217              items != NULL;
5218              items = items->next)
5219         {
5220             if (SP_IS_ARC((SPItem *) items->data))    {
5221                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5222                 repr->setAttribute("sodipodi:open", NULL);
5223                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5224                 modmade = true;
5225             }
5226         }
5227     }
5229     if (modmade) {
5230         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5231                                    _("Arc: Change open/closed"));
5232     }
5234     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5237 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5239     GtkAdjustment *adj;
5240     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5241     gtk_adjustment_set_value(adj, 0.0);
5242     gtk_adjustment_value_changed(adj);
5244     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5245     gtk_adjustment_set_value(adj, 0.0);
5246     gtk_adjustment_value_changed(adj);
5248     spinbutton_defocus( GTK_OBJECT(obj) );
5251 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5252                                       gchar const */*old_value*/, gchar const */*new_value*/,
5253                                       bool /*is_interactive*/, gpointer data)
5255     GObject *tbl = G_OBJECT(data);
5257     // quit if run by the _changed callbacks
5258     if (g_object_get_data( tbl, "freeze" )) {
5259         return;
5260     }
5262     // in turn, prevent callbacks from responding
5263     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5265     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5266     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5268     GtkAdjustment *adj1,*adj2;
5269     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5270     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5271     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5272     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5274     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5276     char const *openstr = NULL;
5277     openstr = repr->attribute("sodipodi:open");
5278     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5280     if (openstr) {
5281         ege_select_one_action_set_active( ocb, 1 );
5282     } else {
5283         ege_select_one_action_set_active( ocb, 0 );
5284     }
5286     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5289 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5290     NULL, /* child_added */
5291     NULL, /* child_removed */
5292     arc_tb_event_attr_changed,
5293     NULL, /* content_changed */
5294     NULL  /* order_changed */
5295 };
5298 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5300     int n_selected = 0;
5301     Inkscape::XML::Node *repr = NULL;
5303     purge_repr_listener( tbl, tbl );
5305     for (GSList const *items = selection->itemList();
5306          items != NULL;
5307          items = items->next)
5308     {
5309         if (SP_IS_ARC((SPItem *) items->data)) {
5310             n_selected++;
5311             repr = SP_OBJECT_REPR((SPItem *) items->data);
5312         }
5313     }
5315     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5317     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5318     if (n_selected == 0) {
5319         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5320     } else if (n_selected == 1) {
5321         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5322         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5324         if (repr) {
5325             g_object_set_data( tbl, "repr", repr );
5326             Inkscape::GC::anchor(repr);
5327             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5328             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5329         }
5330     } else {
5331         // FIXME: implement averaging of all parameters for multiple selected
5332         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5333         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5334         sp_arctb_sensitivize( tbl, 1, 0 );
5335     }
5339 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5341     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5343     EgeAdjustmentAction* eact = 0;
5344     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5347     {
5348         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5349         ege_output_action_set_use_markup( act, TRUE );
5350         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5351         g_object_set_data( holder, "mode_action", act );
5352     }
5354     /* Start */
5355     {
5356         eact = create_adjustment_action( "ArcStartAction",
5357                                          _("Start"), _("Start:"),
5358                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5359                                          "/tools/shapes/arc/start", 0.0,
5360                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5361                                          -360.0, 360.0, 1.0, 10.0,
5362                                          0, 0, 0,
5363                                          sp_arctb_start_value_changed);
5364         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5365     }
5367     /* End */
5368     {
5369         eact = create_adjustment_action( "ArcEndAction",
5370                                          _("End"), _("End:"),
5371                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5372                                          "/tools/shapes/arc/end", 0.0,
5373                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5374                                          -360.0, 360.0, 1.0, 10.0,
5375                                          0, 0, 0,
5376                                          sp_arctb_end_value_changed);
5377         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5378     }
5380     /* Segments / Pie checkbox */
5381     {
5382         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5384         GtkTreeIter iter;
5385         gtk_list_store_append( model, &iter );
5386         gtk_list_store_set( model, &iter,
5387                             0, _("Closed arc"),
5388                             1, _("Switch to segment (closed shape with two radii)"),
5389                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5390                             -1 );
5392         gtk_list_store_append( model, &iter );
5393         gtk_list_store_set( model, &iter,
5394                             0, _("Open Arc"),
5395                             1, _("Switch to arc (unclosed shape)"),
5396                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5397                             -1 );
5399         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5400         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5401         g_object_set_data( holder, "open_action", act );
5403         ege_select_one_action_set_appearance( act, "full" );
5404         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5405         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5406         ege_select_one_action_set_icon_column( act, 2 );
5407         ege_select_one_action_set_icon_size( act, secondarySize );
5408         ege_select_one_action_set_tooltip_column( act, 1  );
5410         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5411         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5412         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5413     }
5415     /* Make Whole */
5416     {
5417         InkAction* inky = ink_action_new( "ArcResetAction",
5418                                           _("Make whole"),
5419                                           _("Make the shape a whole ellipse, not arc or segment"),
5420                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5421                                           secondarySize );
5422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5424         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5425         g_object_set_data( holder, "make_whole", inky );
5426     }
5428     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5429     // sensitivize make whole and open checkbox
5430     {
5431         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5432         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5433         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5434     }
5437     sigc::connection *connection = new sigc::connection(
5438         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5439         );
5440     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5441     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5447 // toggle button callbacks and updaters
5449 //########################
5450 //##      Dropper       ##
5451 //########################
5453 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5454     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5455     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5456     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5457     if ( set_action ) {
5458         if ( gtk_toggle_action_get_active( act ) ) {
5459             gtk_action_set_sensitive( set_action, TRUE );
5460         } else {
5461             gtk_action_set_sensitive( set_action, FALSE );
5462         }
5463     }
5465     spinbutton_defocus(GTK_OBJECT(tbl));
5468 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5469     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5470     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5471     spinbutton_defocus(GTK_OBJECT(tbl));
5475 /**
5476  * Dropper auxiliary toolbar construction and setup.
5477  *
5478  * TODO: Would like to add swatch of current color.
5479  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5480  *       can drag and drop places. Will provide a nice mixing palette.
5481  */
5482 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5484     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5485     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5487     {
5488         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5489         ege_output_action_set_use_markup( act, TRUE );
5490         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5491     }
5493     {
5494         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5495                                                       _("Pick opacity"),
5496                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5497                                                       NULL,
5498                                                       Inkscape::ICON_SIZE_DECORATION );
5499         g_object_set( act, "short_label", _("Pick"), NULL );
5500         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5501         g_object_set_data( holder, "pick_action", act );
5502         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5503         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5504     }
5506     {
5507         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5508                                                       _("Assign opacity"),
5509                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5510                                                       NULL,
5511                                                       Inkscape::ICON_SIZE_DECORATION );
5512         g_object_set( act, "short_label", _("Assign"), NULL );
5513         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5514         g_object_set_data( holder, "set_action", act );
5515         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5516         // make sure it's disabled if we're not picking alpha
5517         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5518         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5519     }
5523 //########################
5524 //##      LPETool       ##
5525 //########################
5527 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5529 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5530 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5532     using namespace Inkscape::LivePathEffect;
5534     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5535     SPEventContext *ec = desktop->event_context;
5536     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5537         return;
5538     }
5540     // only take action if run by the attr_changed listener
5541     if (!g_object_get_data(tbl, "freeze")) {
5542         // in turn, prevent listener from responding
5543         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5545         gint mode = ege_select_one_action_get_active(act);
5546         EffectType type = lpesubtools[mode].type;
5548         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5549         bool success = lpetool_try_construction(lc, type);
5550         if (success) {
5551             // since the construction was already performed, we set the state back to inactive
5552             ege_select_one_action_set_active(act, 0);
5553             mode = 0;
5554         } else {
5555             // switch to the chosen subtool
5556             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5557         }
5559         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5560             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5561             prefs->setInt( "/tools/lpetool/mode", mode );
5562         }
5564         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5565     }
5568 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5570     SPEventContext *ec = selection->desktop()->event_context;
5571     if (!SP_IS_LPETOOL_CONTEXT(ec))
5572         return;
5574     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5577 void
5578 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5580     using namespace Inkscape::LivePathEffect;
5581     SPEventContext *ec = selection->desktop()->event_context;
5582     if (!SP_IS_LPETOOL_CONTEXT(ec))
5583         return;
5584     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5586     lpetool_delete_measuring_items(lc);
5587     lpetool_create_measuring_items(lc, selection);
5589     // activate line segment combo box if a single item with LPELineSegment is selected
5590     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5591     SPItem *item = selection->singleItem();
5592     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5593         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5594         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5595         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5596             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5597             g_object_set_data(tbl, "currentlpe", lpe);
5598             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5599             gtk_action_set_sensitive(w, TRUE);
5600             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5601         } else {
5602             g_object_set_data(tbl, "currentlpe", NULL);
5603             g_object_set_data(tbl, "currentlpeitem", NULL);
5604             gtk_action_set_sensitive(w, FALSE);
5605         }
5606     } else {
5607         g_object_set_data(tbl, "currentlpe", NULL);
5608         g_object_set_data(tbl, "currentlpeitem", NULL);
5609         gtk_action_set_sensitive(w, FALSE);
5610     }
5613 static void
5614 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5615     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5616     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5618     bool show = gtk_toggle_action_get_active( act );
5619     prefs->setBool("/tools/lpetool/show_bbox",  show);
5621     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5622         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5623         lpetool_context_reset_limiting_bbox(lc);
5624     }
5627 static void
5628 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5629     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5630     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5631         return;
5633     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5634     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5635     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5636         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5637         bool show = gtk_toggle_action_get_active( act );
5638         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5639         lpetool_show_measuring_info(lc, show);
5640         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5641     }
5644 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5645     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5646     SPUnit const *unit = tracker->getActiveUnit();
5647     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5648     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5650     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5651     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5652         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5653         lpetool_delete_measuring_items(lc);
5654         lpetool_create_measuring_items(lc);
5655     }
5658 static void
5659 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5660     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5661     Inkscape::Selection *selection = desktop->selection;
5663     Geom::OptRect bbox = selection->bounds();
5665     if (bbox) {
5666         Geom::Point A(bbox->min());
5667         Geom::Point B(bbox->max());
5669         A *= desktop->doc2dt();
5670         B *= desktop->doc2dt();
5672         // TODO: should we provide a way to store points in prefs?
5673         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5674         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5675         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5676         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5677         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5679         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5680     }
5682     gtk_toggle_action_set_active(act, false);
5685 static void
5686 sp_line_segment_build_list(GObject *tbl)
5688     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5690     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5691     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5692     gtk_list_store_clear (model);
5694     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5695     {
5696         GtkTreeIter iter;
5697         gtk_list_store_append( model, &iter );
5698         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5699         gtk_list_store_append( model, &iter );
5700         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5701         gtk_list_store_append( model, &iter );
5702         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5703         gtk_list_store_append( model, &iter );
5704         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5705     }
5707     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5710 static void
5711 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5712     using namespace Inkscape::LivePathEffect;
5714     // quit if run by the attr_changed listener
5715     if (g_object_get_data(tbl, "freeze")) {
5716         return;
5717     }
5719     // in turn, prevent listener from responding
5720     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5722     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5723     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5724     if (lpeitem) {
5725         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5726         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5727         sp_lpe_item_update_patheffect(lpeitem, true, true);
5728     }
5730     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5733 static void
5734 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5735     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5737     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5738         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5739     }
5740     gtk_toggle_action_set_active(act, false);
5743 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5745     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5746     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5747     g_object_set_data(holder, "tracker", tracker);
5748     SPUnit const *unit = tracker->getActiveUnit();
5750     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5751     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5753     /** Automatically create a list of LPEs that get added to the toolbar **/
5754     {
5755         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5757         GtkTreeIter iter;
5759         // the first toggle button represents the state that no subtool is active (remove this when
5760         // this can be modeled by EgeSelectOneAction or some other action)
5761         gtk_list_store_append( model, &iter );
5762         gtk_list_store_set( model, &iter,
5763                             0, _("All inactive"),
5764                             1, _("No geometric tool is active"),
5765                             2, "draw-geometry-inactive",
5766                             -1 );
5768         Inkscape::LivePathEffect::EffectType type;
5769         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5770             type =  lpesubtools[i].type;
5771             gtk_list_store_append( model, &iter );
5772             gtk_list_store_set( model, &iter,
5773                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5774                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5775                                 2, lpesubtools[i].icon_name,
5776                                 -1 );
5777         }
5779         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5780         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5781         g_object_set_data( holder, "lpetool_mode_action", act );
5783         ege_select_one_action_set_appearance( act, "full" );
5784         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5785         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5786         ege_select_one_action_set_icon_column( act, 2 );
5787         ege_select_one_action_set_tooltip_column( act, 1  );
5789         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5790         ege_select_one_action_set_active( act, lpeToolMode );
5791         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5792     }
5794     /* Show limiting bounding box */
5795     {
5796         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5797                                                       _("Show limiting bounding box"),
5798                                                       _("Show bounding box (used to cut infinite lines)"),
5799                                                       "show-bounding-box",
5800                                                       Inkscape::ICON_SIZE_DECORATION );
5801         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5802         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5803         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5804     }
5806     /* Set limiting bounding box to bbox of current selection */
5807     {
5808         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5809                                                       _("Get limiting bounding box from selection"),
5810                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5811                                                       "draw-geometry-set-bounding-box",
5812                                                       Inkscape::ICON_SIZE_DECORATION );
5813         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5814         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5815         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5816     }
5819     /* Combo box to choose line segment type */
5820     {
5821         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5822         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5823         ege_select_one_action_set_appearance (act, "compact");
5824         g_object_set_data (holder, "lpetool_line_segment_action", act );
5826         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5828         sp_line_segment_build_list (holder);
5830         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5831         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5832         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5833     }
5835     /* Display measuring info for selected items */
5836     {
5837         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5838                                                       _("Display measuring info"),
5839                                                       _("Display measuring info for selected items"),
5840                                                       "draw-geometry-show-measuring-info",
5841                                                       Inkscape::ICON_SIZE_DECORATION );
5842         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5843         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5844         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5845     }
5847     // add the units menu
5848     {
5849         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5850         gtk_action_group_add_action( mainActions, act );
5851         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5852         g_object_set_data(holder, "lpetool_units_action", act);
5853         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5854     }
5856     /* Open LPE dialog (to adapt parameters numerically) */
5857     {
5858         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5859                                                       _("Open LPE dialog"),
5860                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5861                                                       "dialog-geometry",
5862                                                       Inkscape::ICON_SIZE_DECORATION );
5863         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5864         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5865         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5866     }
5868     //watch selection
5869     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5871     sigc::connection *c_selection_modified =
5872         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5873                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5874     pool->add_connection ("selection-modified", c_selection_modified);
5876     sigc::connection *c_selection_changed =
5877         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5878                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5879     pool->add_connection ("selection-changed", c_selection_changed);
5882 //########################
5883 //##       Eraser       ##
5884 //########################
5886 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5888     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5889     prefs->setDouble( "/tools/eraser/width", adj->value );
5890     update_presets_list(tbl);
5893 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5895     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5896     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5897     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5898         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5899         prefs->setBool( "/tools/eraser/mode", eraserMode );
5900     }
5902     // only take action if run by the attr_changed listener
5903     if (!g_object_get_data( tbl, "freeze" )) {
5904         // in turn, prevent listener from responding
5905         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5907         if ( eraserMode != 0 ) {
5908         } else {
5909         }
5910         // TODO finish implementation
5912         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5913     }
5916 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5918     {
5919         /* Width */
5920         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5921         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5922         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5923                                                               _("Pen Width"), _("Width:"),
5924                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5925                                                               "/tools/eraser/width", 15,
5926                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5927                                                               1, 100, 1.0, 10.0,
5928                                                               labels, values, G_N_ELEMENTS(labels),
5929                                                               sp_erc_width_value_changed, 1, 0);
5930         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5931         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5932         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5933     }
5935     {
5936         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5938         GtkTreeIter iter;
5939         gtk_list_store_append( model, &iter );
5940         gtk_list_store_set( model, &iter,
5941                             0, _("Delete"),
5942                             1, _("Delete objects touched by the eraser"),
5943                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5944                             -1 );
5946         gtk_list_store_append( model, &iter );
5947         gtk_list_store_set( model, &iter,
5948                             0, _("Cut"),
5949                             1, _("Cut out from objects"),
5950                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5951                             -1 );
5953         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5954         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5955         g_object_set_data( holder, "eraser_mode_action", act );
5957         ege_select_one_action_set_appearance( act, "full" );
5958         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5959         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5960         ege_select_one_action_set_icon_column( act, 2 );
5961         ege_select_one_action_set_tooltip_column( act, 1  );
5963         /// @todo Convert to boolean?
5964         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5965         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5966         ege_select_one_action_set_active( act, eraserMode );
5967         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5968     }
5972 //########################
5973 //##    Text Toolbox    ##
5974 //########################
5975 /*
5976 static void
5977 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5979     //Call back for letter sizing spinbutton
5982 static void
5983 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5985     //Call back for line height spinbutton
5988 static void
5989 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5991     //Call back for horizontal kerning spinbutton
5994 static void
5995 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5997     //Call back for vertical kerning spinbutton
6000 static void
6001 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6003     //Call back for letter rotation spinbutton
6004 }*/
6006 namespace {
6008 void
6009 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6011     // quit if run by the _changed callbacks
6012     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6013         return;
6014     }
6016     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6018     SPStyle *query =
6019         sp_style_new (SP_ACTIVE_DOCUMENT);
6021     int result_family =
6022         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6024     int result_style =
6025         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6027     int result_numbers =
6028         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6030     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6032     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6033     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6034         // there are no texts in selection, read from prefs
6036         sp_style_read_from_prefs(query, "/tools/text");
6038         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6039             // do not reset the toolbar style from prefs if we already did it last time
6040             sp_style_unref(query);
6041             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6042             return;
6043         }
6044         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6045     } else {
6046         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6047     }
6049     if (query->text)
6050     {
6051         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6052             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6053             gtk_entry_set_text (GTK_ENTRY (entry), "");
6055         } else if (query->text->font_specification.value || query->text->font_family.value) {
6057             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6058             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6060             // Get the font that corresponds
6061             Glib::ustring familyName;
6063             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6064             if (font) {
6065                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6066                 font->Unref();
6067                 font = NULL;
6068             }
6070             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6072             Gtk::TreeIter iter;
6073             try {
6074                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6075                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6076                 iter = model->get_iter(path);
6077             } catch (...) {
6078                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6079                 sp_style_unref(query);
6080                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6081                 return;
6082             }
6084             combo->set_active (iter);
6085         }
6087         //Size
6088         {
6089             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6090             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6091             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6092             g_free(str);
6093         }
6095         //Anchor
6096         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6097         {
6098             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6099             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6100             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6101             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6102         }
6103         else
6104         {
6105             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6106             {
6107                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6108                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6109                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6110                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6111             }
6112             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6113             {
6114                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6115                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6116                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6117                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6118             }
6119             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6120             {
6121                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6122                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6123                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6124                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6125             }
6126         }
6128         //Style
6129         {
6130             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6132             gboolean active = gtk_toggle_button_get_active (button);
6133             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));
6135             if (active != check)
6136             {
6137                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6138                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6139                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6140             }
6141         }
6143         {
6144             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6146             gboolean active = gtk_toggle_button_get_active (button);
6147             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6149             if (active != check)
6150             {
6151                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6152                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6153                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6154             }
6155         }
6157         //Orientation
6158         //locking both buttons, changing one affect all group (both)
6159         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6160         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6162         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6163         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6165         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6166         {
6167             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6168         }
6169         else
6170         {
6171             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6172         }
6173         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6174         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6175     }
6177     sp_style_unref(query);
6179     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6182 void
6183 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6185     sp_text_toolbox_selection_changed (selection, tbl);
6188 void
6189 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6191     sp_text_toolbox_selection_changed (NULL, tbl);
6194 void
6195 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6196                                 GObject             *tbl)
6198     // quit if run by the _changed callbacks
6199     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6200         return;
6201     }
6203     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6205     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6206     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6207     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6209     //g_print ("family changed to: %s\n", family);
6211     SPStyle *query =
6212         sp_style_new (SP_ACTIVE_DOCUMENT);
6214     int result_fontspec =
6215         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6217     SPCSSAttr *css = sp_repr_css_attr_new ();
6219     // First try to get the font spec from the stored value
6220     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6222     if (fontSpec.empty()) {
6223         // Construct a new font specification if it does not yet exist
6224         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6225         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6226         fontFromStyle->Unref();
6227     }
6229     if (!fontSpec.empty()) {
6231         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6233         if (!newFontSpec.empty()) {
6235             if (fontSpec != newFontSpec) {
6237                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6239                 if (font) {
6240                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6242                     // Set all the these just in case they were altered when finding the best
6243                     // match for the new family and old style...
6245                     gchar c[256];
6247                     font->Family(c, 256);
6249                     sp_repr_css_set_property (css, "font-family", c);
6251                     font->Attribute( "weight", c, 256);
6252                     sp_repr_css_set_property (css, "font-weight", c);
6254                     font->Attribute("style", c, 256);
6255                     sp_repr_css_set_property (css, "font-style", c);
6257                     font->Attribute("stretch", c, 256);
6258                     sp_repr_css_set_property (css, "font-stretch", c);
6260                     font->Attribute("variant", c, 256);
6261                     sp_repr_css_set_property (css, "font-variant", c);
6263                     font->Unref();
6264                 }
6265             }
6267         } else {
6268             // If the old font on selection (or default) was not existing on the system,
6269             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6270             // setting the family reported by the family chooser.
6272             //g_print ("fallback setting family: %s\n", family);
6273             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6274             sp_repr_css_set_property (css, "font-family", family);
6275         }
6276     }
6278     // If querying returned nothing, set the default style of the tool (for new texts)
6279     if (result_fontspec == QUERY_STYLE_NOTHING)
6280     {
6281         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6282         prefs->mergeStyle("/tools/text/style", css);
6283         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6284     }
6285     else
6286     {
6287         sp_desktop_set_style (desktop, css, true, true);
6288     }
6290     sp_style_unref(query);
6292     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6293                                    _("Text: Change font family"));
6294     sp_repr_css_attr_unref (css);
6296     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6298     // unfreeze
6299     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6301     // focus to canvas
6302     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6306 void
6307 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6308                                    gpointer          data)
6310     if (g_object_get_data (G_OBJECT (button), "block")) return;
6311     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6312     int prop = GPOINTER_TO_INT(data);
6314     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6316     // move the x of all texts to preserve the same bbox
6317     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6318     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6319         if (SP_IS_TEXT((SPItem *) items->data)) {
6320             SPItem *item = SP_ITEM(items->data);
6322             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6323             // below, variable names suggest horizontal move, but we check the writing direction
6324             // and move in the corresponding axis
6325             int axis;
6326             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6327                 axis = NR::X;
6328             } else {
6329                 axis = NR::Y;
6330             }
6332             Geom::OptRect bbox
6333                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6334             if (!bbox)
6335                 continue;
6336             double width = bbox->dimensions()[axis];
6337             // If you want to align within some frame, other than the text's own bbox, calculate
6338             // the left and right (or top and bottom for tb text) slacks of the text inside that
6339             // frame (currently unused)
6340             double left_slack = 0;
6341             double right_slack = 0;
6342             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6343             double move = 0;
6344             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6345                 switch (prop) {
6346                     case 0:
6347                         move = -left_slack;
6348                         break;
6349                     case 1:
6350                         move = width/2 + (right_slack - left_slack)/2;
6351                         break;
6352                     case 2:
6353                         move = width + right_slack;
6354                         break;
6355                 }
6356             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6357                 switch (prop) {
6358                     case 0:
6359                         move = -width/2 - left_slack;
6360                         break;
6361                     case 1:
6362                         move = (right_slack - left_slack)/2;
6363                         break;
6364                     case 2:
6365                         move = width/2 + right_slack;
6366                         break;
6367                 }
6368             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6369                 switch (prop) {
6370                     case 0:
6371                         move = -width - left_slack;
6372                         break;
6373                     case 1:
6374                         move = -width/2 + (right_slack - left_slack)/2;
6375                         break;
6376                     case 2:
6377                         move = right_slack;
6378                         break;
6379                 }
6380             }
6381             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6382             if (axis == NR::X) {
6383                 XY = XY + Geom::Point (move, 0);
6384             } else {
6385                 XY = XY + Geom::Point (0, move);
6386             }
6387             SP_TEXT(item)->attributes.setFirstXY(XY);
6388             SP_OBJECT(item)->updateRepr();
6389             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6390         }
6391     }
6393     SPCSSAttr *css = sp_repr_css_attr_new ();
6394     switch (prop)
6395     {
6396         case 0:
6397         {
6398             sp_repr_css_set_property (css, "text-anchor", "start");
6399             sp_repr_css_set_property (css, "text-align", "start");
6400             break;
6401         }
6402         case 1:
6403         {
6404             sp_repr_css_set_property (css, "text-anchor", "middle");
6405             sp_repr_css_set_property (css, "text-align", "center");
6406             break;
6407         }
6409         case 2:
6410         {
6411             sp_repr_css_set_property (css, "text-anchor", "end");
6412             sp_repr_css_set_property (css, "text-align", "end");
6413             break;
6414         }
6416         case 3:
6417         {
6418             sp_repr_css_set_property (css, "text-anchor", "start");
6419             sp_repr_css_set_property (css, "text-align", "justify");
6420             break;
6421         }
6422     }
6424     SPStyle *query =
6425         sp_style_new (SP_ACTIVE_DOCUMENT);
6426     int result_numbers =
6427         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6429     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6430     if (result_numbers == QUERY_STYLE_NOTHING)
6431     {
6432         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6433         prefs->mergeStyle("/tools/text/style", css);
6434     }
6436     sp_style_unref(query);
6438     sp_desktop_set_style (desktop, css, true, true);
6439     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6440                                    _("Text: Change alignment"));
6441     sp_repr_css_attr_unref (css);
6443     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6446 void
6447 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6448                                gpointer          data)
6450     if (g_object_get_data (G_OBJECT (button), "block")) return;
6452     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6453     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6454     int          prop       = GPOINTER_TO_INT(data);
6455     bool         active     = gtk_toggle_button_get_active (button);
6457     SPStyle *query =
6458         sp_style_new (SP_ACTIVE_DOCUMENT);
6460     int result_fontspec =
6461         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6463     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6464     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6465     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6467     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6468     Glib::ustring newFontSpec = "";
6470     if (fontSpec.empty()) {
6471         // Construct a new font specification if it does not yet exist
6472         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6473         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6474         fontFromStyle->Unref();
6475     }
6477     bool nochange = true;
6478     switch (prop)
6479     {
6480         case 0:
6481         {
6482             if (!fontSpec.empty()) {
6483                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6484                 if (!newFontSpec.empty()) {
6485                     // Don't even set the bold if the font didn't exist on the system
6486                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6487                     nochange = false;
6488                 }
6489             }
6490             // set or reset the button according
6491             if(nochange) {
6492                 gboolean check = gtk_toggle_button_get_active (button);
6494                 if (active != check)
6495                 {
6496                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6497                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6498                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6499                 }
6500             }
6502             break;
6503         }
6505         case 1:
6506         {
6507             if (!fontSpec.empty()) {
6508                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6509                 if (!newFontSpec.empty()) {
6510                     // Don't even set the italic if the font didn't exist on the system
6511                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6512                     nochange = false;
6513                 }
6514             }
6515             if(nochange) {
6516                 gboolean check = gtk_toggle_button_get_active (button);
6518                 if (active != check)
6519                 {
6520                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6521                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6522                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6523                 }
6524             }
6525             break;
6526         }
6527     }
6529     if (!newFontSpec.empty()) {
6530         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6531     }
6533     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6534     if (result_fontspec == QUERY_STYLE_NOTHING)
6535     {
6536         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6537         prefs->mergeStyle("/tools/text/style", css);
6538     }
6540     sp_style_unref(query);
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 font style"));
6545     sp_repr_css_attr_unref (css);
6547     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6550 void
6551 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6552                                      gpointer         data)
6554     if (g_object_get_data (G_OBJECT (button), "block")) {
6555         return;
6556     }
6558     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6559     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6560     int          prop       = GPOINTER_TO_INT(data);
6562     switch (prop)
6563     {
6564         case 0:
6565         {
6566             sp_repr_css_set_property (css, "writing-mode", "lr");
6567             break;
6568         }
6570         case 1:
6571         {
6572             sp_repr_css_set_property (css, "writing-mode", "tb");
6573             break;
6574         }
6575     }
6577     SPStyle *query =
6578         sp_style_new (SP_ACTIVE_DOCUMENT);
6579     int result_numbers =
6580         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6582     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6583     if (result_numbers == QUERY_STYLE_NOTHING)
6584     {
6585         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6586         prefs->mergeStyle("/tools/text/style", css);
6587     }
6589     sp_desktop_set_style (desktop, css, true, true);
6590     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6591                                    _("Text: Change orientation"));
6592     sp_repr_css_attr_unref (css);
6594     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6597 gboolean
6598 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6600     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6601     if (!desktop) return FALSE;
6603     switch (get_group0_keyval (event)) {
6604         case GDK_KP_Enter: // chosen
6605         case GDK_Return:
6606             // unfreeze and update, which will defocus
6607             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6608             sp_text_toolbox_family_changed (NULL, tbl);
6609             return TRUE; // I consumed the event
6610             break;
6611         case GDK_Escape:
6612             // defocus
6613             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6614             return TRUE; // I consumed the event
6615             break;
6616     }
6617     return FALSE;
6620 gboolean
6621 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6623     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6624     if (!desktop) return FALSE;
6626     switch (get_group0_keyval (event)) {
6627         case GDK_KP_Enter:
6628         case GDK_Return:
6629         case GDK_Escape: // defocus
6630             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6631             return TRUE; // I consumed the event
6632             break;
6633         case GDK_w:
6634         case GDK_W:
6635             if (event->state & GDK_CONTROL_MASK) {
6636                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6637                 return TRUE; // I consumed the event
6638             }
6639             break;
6640     }
6641     return FALSE;
6645 void
6646 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6647                                GObject     *tbl)
6649      // quit if run by the _changed callbacks
6650     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6651         return;
6652     }
6654     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6656    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6658     // If this is not from selecting a size in the list (in which case get_active will give the
6659     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6660     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6661     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6662    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6663         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6664         return;
6665    }
6667     gdouble value = -1;
6668     {
6669         gchar *endptr;
6670         gchar *const text = gtk_combo_box_get_active_text(cbox);
6671         if (text) {
6672             value = g_strtod(text, &endptr);
6673             if (endptr == text) {  // Conversion failed, non-numeric input.
6674                 value = -1;
6675             }
6676             g_free(text);
6677         }
6678     }
6679     if (value <= 0) {
6680         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6681         return; // could not parse value
6682     }
6684     SPCSSAttr *css = sp_repr_css_attr_new ();
6685     Inkscape::CSSOStringStream osfs;
6686     osfs << value;
6687     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6689     SPStyle *query =
6690         sp_style_new (SP_ACTIVE_DOCUMENT);
6691     int result_numbers =
6692         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6694     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6695     if (result_numbers == QUERY_STYLE_NOTHING)
6696     {
6697         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6698         prefs->mergeStyle("/tools/text/style", css);
6699     }
6701     sp_style_unref(query);
6703     sp_desktop_set_style (desktop, css, true, true);
6704     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6705                                    _("Text: Change font size"));
6706     sp_repr_css_attr_unref (css);
6708     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6710     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6713 gboolean
6714 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6716     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6717     if (!desktop) return FALSE;
6719     if (!g_object_get_data (tbl, "esc-pressed")) {
6720         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6721         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6722         sp_text_toolbox_size_changed (cbox, tbl);
6723         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6724     }
6725     return FALSE; // I consumed the event
6729 gboolean
6730 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6732     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6733     if (!desktop) return FALSE;
6735     switch (get_group0_keyval (event)) {
6736         case GDK_Escape: // defocus
6737             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6738             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6739             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6740             return TRUE; // I consumed the event
6741             break;
6742         case GDK_Return: // defocus
6743         case GDK_KP_Enter:
6744             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6745             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6746             sp_text_toolbox_size_changed (cbox, tbl);
6747             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6748             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6749             return TRUE; // I consumed the event
6750             break;
6751     }
6752     return FALSE;
6755 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6756 // does not work!
6757 gboolean
6758 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6759                                  GdkEventFocus    */*event*/,
6760                                  GObject          *tbl)
6762     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6763     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6764     return FALSE;
6767 gboolean
6768 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6769                                  GdkEventFocus    */*event*/,
6770                                  GObject          *tbl)
6772     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6773     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6774     return FALSE;
6777 void
6778 cell_data_func  (GtkCellLayout */*cell_layout*/,
6779                  GtkCellRenderer   *cell,
6780                  GtkTreeModel      *tree_model,
6781                  GtkTreeIter       *iter,
6782                  gpointer           /*data*/)
6784     gchar *family;
6785     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6786     gchar *const family_escaped = g_markup_escape_text(family, -1);
6788     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6789     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6790     if (show_sample) {
6792         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6793         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6795     std::stringstream markup;
6796     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6797            << family_escaped << "'>" << sample_escaped << "</span>";
6798     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6800         g_free(sample_escaped);
6801     } else {
6802         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6803     }
6805     g_free(family);
6806     g_free(family_escaped);
6809 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6810                                                 GtkTreeModel       *model,
6811                                                 GtkTreeIter        *iter,
6812                                                 GObject            *tbl)
6814     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6815     // press Enter again after choosing a completion)
6816     gchar *family = 0;
6817     gtk_tree_model_get(model, iter, 0, &family, -1);
6819     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6820     gtk_entry_set_text (GTK_ENTRY (entry), family);
6822     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6823     sp_text_toolbox_family_changed (NULL, tbl);
6824     return TRUE;
6828 static void
6829 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6830     GtkEntry *entry;
6831     GtkEntryCompletion *completion;
6832     GtkTreeModel *model;
6834     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6835     completion = gtk_entry_completion_new();
6836     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6837     gtk_entry_completion_set_model(completion, model);
6838     gtk_entry_completion_set_text_column(completion, 0);
6839     gtk_entry_completion_set_inline_completion(completion, FALSE);
6840     gtk_entry_completion_set_inline_selection(completion, FALSE);
6841     gtk_entry_completion_set_popup_completion(completion, TRUE);
6842     gtk_entry_set_completion(entry, completion);
6844     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6846     g_object_unref(completion);
6849 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6850                                       void */*property*/,
6851                                       GObject *tbl)
6853   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6855   gboolean shown;
6856   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6857   if (shown) {
6858          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6859          //g_print("POP: notify: SHOWN\n");
6860   } else {
6861          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6863          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6864          // find out if the drop down was closed by Enter and if so, manually update (only
6865          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6866          GdkEvent *ev = gtk_get_current_event();
6867          if (ev) {
6868              //g_print ("ev type: %d\n", ev->type);
6869              if (ev->type == GDK_KEY_PRESS) {
6870                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6871                      case GDK_KP_Enter: // chosen
6872                      case GDK_Return:
6873                      {
6874                          // make sure the chosen one is inserted into the entry
6875                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6876                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6877                          GtkTreeIter iter;
6878                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6879                          if (has_active) {
6880                              gchar *family;
6881                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6882                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6883                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6884                          }
6886                          // update
6887                          sp_text_toolbox_family_changed (NULL, tbl);
6888                          break;
6889                      }
6890                  }
6891              }
6892          }
6894          // regardless of whether we updated, defocus the widget
6895          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6896          if (desktop)
6897              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6898          //g_print("POP: notify: HIDDEN\n");
6899   }
6902 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6904     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6905     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6907     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6908     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6910     GtkTooltips *tt = gtk_tooltips_new();
6912     ////////////Family
6913     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6914     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6916     gtk_rc_parse_string (
6917        "style \"dropdown-as-list-style\"\n"
6918        "{\n"
6919        "    GtkComboBox::appears-as-list = 1\n"
6920        "}\n"
6921        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6922     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6923     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6925     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6927     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6929     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6930     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6932     // expand the field a bit so as to view more of the previews in the drop-down
6933     GtkRequisition req;
6934     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6935     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6937     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6938     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6940     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6941     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6942              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6943     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6944     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6945     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6947     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6948     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6950     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6951     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6952     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6953     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6955     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6956     GtkWidget *box = gtk_event_box_new ();
6957     gtk_container_add (GTK_CONTAINER (box), image);
6958     gtk_toolbar_append_widget( tbl, box, "", "");
6959     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6960     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6961     gtk_widget_hide (GTK_WIDGET (box));
6962     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6964     ////////////Size
6965     gchar const *const sizes[] = {
6966         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6967         "16", "18", "20", "22", "24", "28",
6968         "32", "36", "40", "48", "56", "64", "72", "144"
6969     };
6971     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6972     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6973         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6974     }
6975     gtk_widget_set_size_request (cbox, 80, -1);
6976     gtk_toolbar_append_widget( tbl, cbox, "", "");
6977     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6978     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6979     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6980     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6982     ////////////Text anchor
6983     GtkWidget *group   = gtk_radio_button_new (NULL);
6984     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6985     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6987     // left
6988     GtkWidget *rbutton = group;
6989     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6990     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6991     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6993     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6994     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6995     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6996     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6998     // center
6999     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7000     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7001     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7002     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7004     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7005     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7006     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7007     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7009     // right
7010     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7011     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7012     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7013     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7015     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7016     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7017     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7018     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7020     // fill
7021     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7022     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7023     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7024     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7026     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7027     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7028     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7029     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7031     gtk_toolbar_append_widget( tbl, row, "", "");
7033     //spacer
7034     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7036     ////////////Text style
7037     row = gtk_hbox_new (FALSE, 4);
7039     // bold
7040     rbutton = gtk_toggle_button_new ();
7041     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7042     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7043     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7044     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7046     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7047     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7048     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7050     // italic
7051     rbutton = gtk_toggle_button_new ();
7052     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7053     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7054     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7055     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7057     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7058     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7059     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7061     gtk_toolbar_append_widget( tbl, row, "", "");
7063     //spacer
7064     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7066     // Text orientation
7067     group   = gtk_radio_button_new (NULL);
7068     row     = gtk_hbox_new (FALSE, 4);
7069     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7071     // horizontal
7072     rbutton = group;
7073     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7074     gtk_container_add           (GTK_CONTAINER (rbutton),
7075                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7076     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7077     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7079     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7080     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7081     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7083     // vertical
7084     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7085     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7086     gtk_container_add           (GTK_CONTAINER (rbutton),
7087                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7088     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7089     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7091     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7092     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7093     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7094     gtk_toolbar_append_widget( tbl, row, "", "" );
7097     //watch selection
7098     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7100     sigc::connection *c_selection_changed =
7101         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7102                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7103     pool->add_connection ("selection-changed", c_selection_changed);
7105     sigc::connection *c_selection_modified =
7106         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7107                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7108     pool->add_connection ("selection-modified", c_selection_modified);
7110     sigc::connection *c_subselection_changed =
7111         new sigc::connection (desktop->connectToolSubselectionChanged
7112                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7113     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7115     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7118     gtk_widget_show_all( GTK_WIDGET(tbl) );
7120     return GTK_WIDGET(tbl);
7121 } // end of sp_text_toolbox_new()
7123 }//<unnamed> namespace
7126 //#########################
7127 //##      Connector      ##
7128 //#########################
7130 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7132     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7133     prefs->setBool("/tools/connector/mode",
7134                 gtk_toggle_action_get_active( act ));
7137 static void sp_connector_path_set_avoid(void)
7139     cc_selection_set_avoid(true);
7143 static void sp_connector_path_set_ignore(void)
7145     cc_selection_set_avoid(false);
7148 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7150     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7151     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7152     SPDocument *doc = sp_desktop_document(desktop);
7154     if (!sp_document_get_undo_sensitive(doc))
7155     {
7156         return;
7157     }
7160     // quit if run by the _changed callbacks
7161     if (g_object_get_data( tbl, "freeze" )) {
7162         return;
7163     }
7165     // in turn, prevent callbacks from responding
7166     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7168     bool is_orthog = gtk_toggle_action_get_active( act );
7169     gchar orthog_str[] = "orthogonal";
7170     gchar polyline_str[] = "polyline";
7171     gchar *value = is_orthog ? orthog_str : polyline_str ;
7173     bool modmade = false;
7174     GSList *l = (GSList *) selection->itemList();
7175     while (l) {
7176         SPItem *item = (SPItem *) l->data;
7178         if (cc_item_is_connector(item)) {
7179             sp_object_setAttribute(item, "inkscape:connector-type",
7180                     value, false);
7181             item->avoidRef->handleSettingChange();
7182             modmade = true;
7183         }
7184         l = l->next;
7185     }
7187     if (!modmade)
7188     {
7189         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7190         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7191     }
7193     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7194             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7196     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7199 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7201     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7202     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7203     SPDocument *doc = sp_desktop_document(desktop);
7205     if (!sp_document_get_undo_sensitive(doc))
7206     {
7207         return;
7208     }
7211     // quit if run by the _changed callbacks
7212     if (g_object_get_data( tbl, "freeze" )) {
7213         return;
7214     }
7216     // in turn, prevent callbacks from responding
7217     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7219     gdouble newValue = gtk_adjustment_get_value(adj);
7220     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7221     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7223     bool modmade = false;
7224     GSList *l = (GSList *) selection->itemList();
7225     while (l) {
7226         SPItem *item = (SPItem *) l->data;
7228         if (cc_item_is_connector(item)) {
7229             sp_object_setAttribute(item, "inkscape:connector-curvature",
7230                     value, false);
7231             item->avoidRef->handleSettingChange();
7232             modmade = true;
7233         }
7234         l = l->next;
7235     }
7237     if (!modmade)
7238     {
7239         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7240         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7241     }
7243     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7244             _("Change connector curvature"));
7246     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7250 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7252     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7253     SPDocument *doc = sp_desktop_document(desktop);
7255     if (!sp_document_get_undo_sensitive(doc))
7256     {
7257         return;
7258     }
7260     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7262     if ( !repr->attribute("inkscape:connector-spacing") &&
7263             ( adj->value == defaultConnSpacing )) {
7264         // Don't need to update the repr if the attribute doesn't
7265         // exist and it is being set to the default value -- as will
7266         // happen at startup.
7267         return;
7268     }
7270     // quit if run by the attr_changed listener
7271     if (g_object_get_data( tbl, "freeze" )) {
7272         return;
7273     }
7275     // in turn, prevent listener from responding
7276     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7278     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7279     SP_OBJECT(desktop->namedview)->updateRepr();
7281     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7282     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7283         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7284         Geom::Matrix m = Geom::identity();
7285         avoid_item_move(&m, item);
7286     }
7288     if (items) {
7289         g_slist_free(items);
7290     }
7292     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7293             _("Change connector spacing"));
7295     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7298 static void sp_connector_graph_layout(void)
7300     if (!SP_ACTIVE_DESKTOP) return;
7301     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7303     // hack for clones, see comment in align-and-distribute.cpp
7304     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7305     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7307     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7309     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7311     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7314 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7316     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7317     prefs->setBool("/tools/connector/directedlayout",
7318                 gtk_toggle_action_get_active( act ));
7321 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7323     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7324     prefs->setBool("/tools/connector/avoidoverlaplayout",
7325                 gtk_toggle_action_get_active( act ));
7329 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7331     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7332     prefs->setDouble("/tools/connector/length", adj->value);
7335 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7336                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7337                                             bool /*is_interactive*/, gpointer data)
7339     GtkWidget *tbl = GTK_WIDGET(data);
7341     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7342         return;
7343     }
7344     if (strcmp(name, "inkscape:connector-spacing") == 0)
7345     {
7346         GtkAdjustment *adj = (GtkAdjustment*)
7347                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7348         gdouble spacing = defaultConnSpacing;
7349         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7351         gtk_adjustment_set_value(adj, spacing);
7352         gtk_adjustment_value_changed(adj);
7353     }
7355     spinbutton_defocus(GTK_OBJECT(tbl));
7358 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7360     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7361     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7363     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7364         cc_create_connection_point(cc);
7367 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7369     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7370     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7372     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7373         cc_remove_connection_point(cc);
7376 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7377     NULL, /* child_added */
7378     NULL, /* child_removed */
7379     connector_tb_event_attr_changed,
7380     NULL, /* content_changed */
7381     NULL  /* order_changed */
7382 };
7384 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7386     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7387     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7388     SPItem *item = selection->singleItem();
7389     if (SP_IS_PATH(item))
7390     {
7391         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7392         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7393         gtk_toggle_action_set_active(act, is_orthog);
7394         gtk_adjustment_set_value(adj, curvature);
7395     }
7399 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7401     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7402     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7404     // Editing mode toggle button
7405     {
7406         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7407                                                       _("EditMode"),
7408                                                       _("Switch between connection point editing and connector drawing mode"),
7409                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7410                                                       Inkscape::ICON_SIZE_DECORATION );
7411         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7413         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7414         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7415         g_object_set_data( holder, "mode", act );
7416         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7417     }
7420     {
7421         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7422                                           _("Avoid"),
7423                                           _("Make connectors avoid selected objects"),
7424                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7425                                           secondarySize );
7426         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7427         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7428     }
7430     {
7431         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7432                                           _("Ignore"),
7433                                           _("Make connectors ignore selected objects"),
7434                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7435                                           secondarySize );
7436         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7437         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7438     }
7440     // Orthogonal connectors toggle button
7441     {
7442         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7443                                                       _("Orthogonal"),
7444                                                       _("Make connector orthogonal or polyline"),
7445                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7446                                                       Inkscape::ICON_SIZE_DECORATION );
7447         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7449         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7450         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7451         g_object_set_data( holder, "orthogonal", act );
7452         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7453     }
7455     EgeAdjustmentAction* eact = 0;
7456     // Curvature spinbox
7457     eact = create_adjustment_action( "ConnectorCurvatureAction",
7458                                     _("Connector Curvature"), _("Curvature:"),
7459                                     _("The amount of connectors curvature"),
7460                                     "/tools/connector/curvature", defaultConnCurvature,
7461                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7462                                     0, 100, 1.0, 10.0,
7463                                     0, 0, 0,
7464                                     connector_curvature_changed, 1, 0 );
7465     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7467     // Spacing spinbox
7468     eact = create_adjustment_action( "ConnectorSpacingAction",
7469                                     _("Connector Spacing"), _("Spacing:"),
7470                                     _("The amount of space left around objects by auto-routing connectors"),
7471                                     "/tools/connector/spacing", defaultConnSpacing,
7472                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7473                                     0, 100, 1.0, 10.0,
7474                                     0, 0, 0,
7475                                     connector_spacing_changed, 1, 0 );
7476     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7478     // Graph (connector network) layout
7479     {
7480         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7481                                           _("Graph"),
7482                                           _("Nicely arrange selected connector network"),
7483                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7484                                           secondarySize );
7485         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7486         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7487     }
7489     // Default connector length spinbox
7490     eact = create_adjustment_action( "ConnectorLengthAction",
7491                                      _("Connector Length"), _("Length:"),
7492                                      _("Ideal length for connectors when layout is applied"),
7493                                      "/tools/connector/length", 100,
7494                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7495                                      10, 1000, 10.0, 100.0,
7496                                      0, 0, 0,
7497                                      connector_length_changed, 1, 0 );
7498     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7501     // Directed edges toggle button
7502     {
7503         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7504                                                       _("Downwards"),
7505                                                       _("Make connectors with end-markers (arrows) point downwards"),
7506                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7507                                                       Inkscape::ICON_SIZE_DECORATION );
7508         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7510         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7511         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7513         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7514         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7515         );
7516     }
7518     // Avoid overlaps toggle button
7519     {
7520         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7521                                                       _("Remove overlaps"),
7522                                                       _("Do not allow overlapping shapes"),
7523                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7524                                                       Inkscape::ICON_SIZE_DECORATION );
7525         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7527         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7528         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7530         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7531     }
7534     // New connection point button
7535     {
7536         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7537                                           _("New connection point"),
7538                                           _("Add a new connection point to the currently selected item"),
7539                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7540                                           secondarySize );
7541         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7542         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7543     }
7545     // Remove selected connection point button
7547     {
7548         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7549                                           _("Remove connection point"),
7550                                           _("Remove the currently selected connection point"),
7551                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7552                                           secondarySize );
7553         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7554         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7555     }
7558     // Code to watch for changes to the connector-spacing attribute in
7559     // the XML.
7560     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7561     g_assert(repr != NULL);
7563     purge_repr_listener( holder, holder );
7565     if (repr) {
7566         g_object_set_data( holder, "repr", repr );
7567         Inkscape::GC::anchor(repr);
7568         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7569         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7570     }
7571 } // end of sp_connector_toolbox_prep()
7574 //#########################
7575 //##     Paintbucket     ##
7576 //#########################
7578 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7580     gint channels = ege_select_one_action_get_active( act );
7581     flood_channels_set_channels( channels );
7584 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7586     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7587     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7590 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7592     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7593     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7596 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7598     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7599     SPUnit const *unit = tracker->getActiveUnit();
7600     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7602     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7603     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7606 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7608     // FIXME: make defaults settable via Inkscape Options
7609     struct KeyValue {
7610         char const *key;
7611         double value;
7612     } const key_values[] = {
7613         {"threshold", 15},
7614         {"offset", 0.0}
7615     };
7617     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7618         KeyValue const &kv = key_values[i];
7619         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7620         if ( adj ) {
7621             gtk_adjustment_set_value(adj, kv.value);
7622         }
7623     }
7625     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7626     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7627     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7628     ege_select_one_action_set_active( autogap_action, 0 );
7631 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7633     EgeAdjustmentAction* eact = 0;
7634     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7636     {
7637         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7639         GList* items = 0;
7640         gint count = 0;
7641         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7642         {
7643             GtkTreeIter iter;
7644             gtk_list_store_append( model, &iter );
7645             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7646             count++;
7647         }
7648         g_list_free( items );
7649         items = 0;
7650         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7651         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7652         ege_select_one_action_set_appearance( act1, "compact" );
7653         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7654         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7655         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7656         g_object_set_data( holder, "channels_action", act1 );
7657     }
7659     // Spacing spinbox
7660     {
7661         eact = create_adjustment_action(
7662             "ThresholdAction",
7663             _("Fill Threshold"), _("Threshold:"),
7664             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7665             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7666             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7667             0, 0, 0,
7668             paintbucket_threshold_changed, 1, 0 );
7670         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7671         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7672     }
7674     // Create the units menu.
7675     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7676     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7677     if (!stored_unit.empty())
7678         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7679     g_object_set_data( holder, "tracker", tracker );
7680     {
7681         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7682         gtk_action_group_add_action( mainActions, act );
7683     }
7685     // Offset spinbox
7686     {
7687         eact = create_adjustment_action(
7688             "OffsetAction",
7689             _("Grow/shrink by"), _("Grow/shrink by:"),
7690             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7691             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7692             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7693             0, 0, 0,
7694             paintbucket_offset_changed, 1, 2);
7695         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7697         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7698     }
7700     /* Auto Gap */
7701     {
7702         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7704         GList* items = 0;
7705         gint count = 0;
7706         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7707         {
7708             GtkTreeIter iter;
7709             gtk_list_store_append( model, &iter );
7710             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7711             count++;
7712         }
7713         g_list_free( items );
7714         items = 0;
7715         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7716         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7717         ege_select_one_action_set_appearance( act2, "compact" );
7718         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7719         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7720         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7721         g_object_set_data( holder, "autogap_action", act2 );
7722     }
7724     /* Reset */
7725     {
7726         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7727                                           _("Defaults"),
7728                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7729                                           GTK_STOCK_CLEAR );
7730         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7731         gtk_action_group_add_action( mainActions, act );
7732         gtk_action_set_sensitive( act, TRUE );
7733     }
7737 /*
7738   Local Variables:
7739   mode:c++
7740   c-file-style:"stroustrup"
7741   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7742   indent-tabs-mode:nil
7743   fill-column:99
7744   End:
7745 */
7746 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :