Code

f4913c01930a8dbb3727a06c1ee8e6fdfd527995
[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 GtkWidget *
839 sp_tool_toolbox_new()
841     GtkTooltips *tt = gtk_tooltips_new();
842     GtkWidget* tb = gtk_toolbar_new();
843     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
844     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
846     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
847     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
849     gtk_widget_set_sensitive(tb, FALSE);
851     GtkWidget *hb = gtk_handle_box_new();
852     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
853     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
854     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
856     gtk_container_add(GTK_CONTAINER(hb), tb);
857     gtk_widget_show(GTK_WIDGET(tb));
859     sigc::connection* conn = new sigc::connection;
860     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
862     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
863     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
865     return hb;
868 GtkWidget *
869 sp_aux_toolbox_new()
871     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
873     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
875     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
877     gtk_widget_set_sensitive(tb, FALSE);
879     GtkWidget *hb = gtk_handle_box_new();
880     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
881     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
882     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
884     gtk_container_add(GTK_CONTAINER(hb), tb);
885     gtk_widget_show(GTK_WIDGET(tb));
887     sigc::connection* conn = new sigc::connection;
888     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
890     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
891     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
893     return hb;
896 //####################################
897 //# Commands Bar
898 //####################################
900 GtkWidget *
901 sp_commands_toolbox_new()
903     GtkWidget *tb = gtk_toolbar_new();
905     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
906     gtk_widget_set_sensitive(tb, FALSE);
908     GtkWidget *hb = gtk_handle_box_new();
909     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
910     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
911     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
913     gtk_container_add(GTK_CONTAINER(hb), tb);
914     gtk_widget_show(GTK_WIDGET(tb));
916     sigc::connection* conn = new sigc::connection;
917     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
919     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
920     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
922     return hb;
925 GtkWidget *
926 sp_snap_toolbox_new()
928     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
929     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
930     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
932     //GtkWidget *tb = gtk_toolbar_new();
933     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
935     gtk_widget_set_sensitive(tb, FALSE);
937     GtkWidget *hb = gtk_handle_box_new();
938     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
939     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
940     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
942     gtk_container_add(GTK_CONTAINER(hb), tb);
943     gtk_widget_show(GTK_WIDGET(tb));
945     sigc::connection* conn = new sigc::connection;
946     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
948     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
949     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
951     return hb;
954 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
955                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
956                                                        Glib::ustring const &path, gdouble def,
957                                                        GtkWidget *focusTarget,
958                                                        GtkWidget *us,
959                                                        GObject *dataKludge,
960                                                        gboolean altx, gchar const *altx_mark,
961                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
962                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
963                                                        void (*callback)(GtkAdjustment *, GObject *),
964                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
966     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
967     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
968                                                              lower, upper, step, page, 0 ) );
969     if (us) {
970         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
971     }
973     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
975     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
976     if ( shortLabel ) {
977         g_object_set( act, "short_label", shortLabel, NULL );
978     }
980     if ( (descrCount > 0) && descrLabels && descrValues ) {
981         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
982     }
984     if ( focusTarget ) {
985         ege_adjustment_action_set_focuswidget( act, focusTarget );
986     }
988     if ( altx && altx_mark ) {
989         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
990     }
992     if ( dataKludge ) {
993         // Rather lame, but it's the only place where we need to get the entry name
994         // but we don't have an Entry
995         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
996     }
998     // Using a cast just to make sure we pass in the right kind of function pointer
999     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1001     return act;
1005 //####################################
1006 //# node editing callbacks
1007 //####################################
1009 /**
1010  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1011  */
1012 static ShapeEditor *get_current_shape_editor()
1014     if (!SP_ACTIVE_DESKTOP) {
1015         return NULL;
1016     }
1018     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1020     if (!SP_IS_NODE_CONTEXT(event_context)) {
1021         return NULL;
1022     }
1024     return event_context->shape_editor;
1028 void
1029 sp_node_path_edit_add(void)
1031     ShapeEditor *shape_editor = get_current_shape_editor();
1032     if (shape_editor) shape_editor->add_node();
1035 void
1036 sp_node_path_edit_delete(void)
1038     ShapeEditor *shape_editor = get_current_shape_editor();
1039     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1042 void
1043 sp_node_path_edit_delete_segment(void)
1045     ShapeEditor *shape_editor = get_current_shape_editor();
1046     if (shape_editor) shape_editor->delete_segment();
1049 void
1050 sp_node_path_edit_break(void)
1052     ShapeEditor *shape_editor = get_current_shape_editor();
1053     if (shape_editor) shape_editor->break_at_nodes();
1056 void
1057 sp_node_path_edit_join(void)
1059     ShapeEditor *shape_editor = get_current_shape_editor();
1060     if (shape_editor) shape_editor->join_nodes();
1063 void
1064 sp_node_path_edit_join_segment(void)
1066     ShapeEditor *shape_editor = get_current_shape_editor();
1067     if (shape_editor) shape_editor->join_segments();
1070 void
1071 sp_node_path_edit_toline(void)
1073     ShapeEditor *shape_editor = get_current_shape_editor();
1074     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1077 void
1078 sp_node_path_edit_tocurve(void)
1080     ShapeEditor *shape_editor = get_current_shape_editor();
1081     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1084 void
1085 sp_node_path_edit_cusp(void)
1087     ShapeEditor *shape_editor = get_current_shape_editor();
1088     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1091 void
1092 sp_node_path_edit_smooth(void)
1094     ShapeEditor *shape_editor = get_current_shape_editor();
1095     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1098 void
1099 sp_node_path_edit_symmetrical(void)
1101     ShapeEditor *shape_editor = get_current_shape_editor();
1102     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1105 void
1106 sp_node_path_edit_auto(void)
1108     ShapeEditor *shape_editor = get_current_shape_editor();
1109     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1112 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1113     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1114     bool show = gtk_toggle_action_get_active( act );
1115     prefs->setBool("/tools/nodes/show_handles",  show);
1116     ShapeEditor *shape_editor = get_current_shape_editor();
1117     if (shape_editor) shape_editor->show_handles(show);
1120 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1121     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1122     bool show = gtk_toggle_action_get_active( act );
1123     prefs->setBool("/tools/nodes/show_helperpath",  show);
1124     ShapeEditor *shape_editor = get_current_shape_editor();
1125     if (shape_editor) shape_editor->show_helperpath(show);
1128 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1129     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1132 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1133     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1136 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1137     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1140 /* is called when the node selection is modified */
1141 static void
1142 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1144     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1145     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1146     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1147     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1149     // quit if run by the attr_changed listener
1150     if (g_object_get_data( tbl, "freeze" )) {
1151         return;
1152     }
1154     // in turn, prevent listener from responding
1155     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1157     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1158     SPUnit const *unit = tracker->getActiveUnit();
1160     ShapeEditor *shape_editor = get_current_shape_editor();
1161     if (shape_editor && shape_editor->has_nodepath()) {
1162         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1163         int n_selected = 0;
1164         if (nodepath) {
1165             n_selected = nodepath->numSelected();
1166         }
1168         if (n_selected == 0) {
1169             gtk_action_set_sensitive(xact, FALSE);
1170             gtk_action_set_sensitive(yact, FALSE);
1171         } else {
1172             gtk_action_set_sensitive(xact, TRUE);
1173             gtk_action_set_sensitive(yact, TRUE);
1174             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1175             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1177             if (n_selected == 1) {
1178                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1179                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1180                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1181                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1182                 }
1183             } else {
1184                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1185                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1186                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1187                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1188                        selected nodes don't coincide (in this case we use the coordinates of the center
1189                        of the bounding box). So the entries are never set to zero. */
1190                     // FIXME: Maybe we should clear the entry if several nodes are selected
1191                     //        instead of providing a kind of average value
1192                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1193                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1194                 }
1195             }
1196         }
1197     } else {
1198         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1199         gtk_action_set_sensitive(xact, FALSE);
1200         gtk_action_set_sensitive(yact, FALSE);
1201     }
1203     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1206 static void
1207 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1209     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1210     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1212     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1213     SPUnit const *unit = tracker->getActiveUnit();
1215     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1216         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1217     }
1219     // quit if run by the attr_changed listener
1220     if (g_object_get_data( tbl, "freeze" )) {
1221         return;
1222     }
1224     // in turn, prevent listener from responding
1225     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1227     ShapeEditor *shape_editor = get_current_shape_editor();
1228     if (shape_editor && shape_editor->has_nodepath()) {
1229         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1230         if (!strcmp(value_name, "x")) {
1231             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1232         }
1233         if (!strcmp(value_name, "y")) {
1234             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1235         }
1236     }
1238     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1241 static void
1242 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1244     sp_node_path_value_changed(adj, tbl, "x");
1247 static void
1248 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1250     sp_node_path_value_changed(adj, tbl, "y");
1253 void
1254 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1256     {
1257     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1258     SPItem *item = selection->singleItem();
1259     if (item && SP_IS_LPE_ITEM(item)) {
1260        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1261            gtk_action_set_sensitive(w, TRUE);
1262        } else {
1263            gtk_action_set_sensitive(w, FALSE);
1264        }
1265     } else {
1266        gtk_action_set_sensitive(w, FALSE);
1267     }
1268     }
1270     {
1271     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1272     SPItem *item = selection->singleItem();
1273     if (item && item->clip_ref && item->clip_ref->getObject()) {
1274        gtk_action_set_sensitive(w, TRUE);
1275     } else {
1276        gtk_action_set_sensitive(w, FALSE);
1277     }
1278     }
1280     {
1281     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1282     SPItem *item = selection->singleItem();
1283     if (item && item->mask_ref && item->mask_ref->getObject()) {
1284        gtk_action_set_sensitive(w, TRUE);
1285     } else {
1286        gtk_action_set_sensitive(w, FALSE);
1287     }
1288     }
1291 void
1292 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1294     sp_node_toolbox_sel_changed (selection, tbl);
1299 //################################
1300 //##    Node Editing Toolbox    ##
1301 //################################
1303 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1305     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1306     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1307     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1308     g_object_set_data( holder, "tracker", tracker );
1310     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1312     {
1313         InkAction* inky = ink_action_new( "NodeInsertAction",
1314                                           _("Insert node"),
1315                                           _("Insert new nodes into selected segments"),
1316                                           INKSCAPE_ICON_NODE_ADD,
1317                                           secondarySize );
1318         g_object_set( inky, "short_label", _("Insert"), NULL );
1319         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1320         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1321     }
1323     {
1324         InkAction* inky = ink_action_new( "NodeDeleteAction",
1325                                           _("Delete node"),
1326                                           _("Delete selected nodes"),
1327                                           INKSCAPE_ICON_NODE_DELETE,
1328                                           secondarySize );
1329         g_object_set( inky, "short_label", _("Delete"), NULL );
1330         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1331         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1332     }
1334     {
1335         InkAction* inky = ink_action_new( "NodeJoinAction",
1336                                           _("Join endnodes"),
1337                                           _("Join selected endnodes"),
1338                                           INKSCAPE_ICON_NODE_JOIN,
1339                                           secondarySize );
1340         g_object_set( inky, "short_label", _("Join"), NULL );
1341         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1342         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1343     }
1345     {
1346         InkAction* inky = ink_action_new( "NodeBreakAction",
1347                                           _("Break nodes"),
1348                                           _("Break path at selected nodes"),
1349                                           INKSCAPE_ICON_NODE_BREAK,
1350                                           secondarySize );
1351         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1352         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1353     }
1356     {
1357         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1358                                           _("Join with segment"),
1359                                           _("Join selected endnodes with a new segment"),
1360                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1361                                           secondarySize );
1362         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1363         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1364     }
1366     {
1367         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1368                                           _("Delete segment"),
1369                                           _("Delete segment between two non-endpoint nodes"),
1370                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1371                                           secondarySize );
1372         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1373         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1374     }
1376     {
1377         InkAction* inky = ink_action_new( "NodeCuspAction",
1378                                           _("Node Cusp"),
1379                                           _("Make selected nodes corner"),
1380                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1381                                           secondarySize );
1382         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1383         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1384     }
1386     {
1387         InkAction* inky = ink_action_new( "NodeSmoothAction",
1388                                           _("Node Smooth"),
1389                                           _("Make selected nodes smooth"),
1390                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1391                                           secondarySize );
1392         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1393         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1394     }
1396     {
1397         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1398                                           _("Node Symmetric"),
1399                                           _("Make selected nodes symmetric"),
1400                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1401                                           secondarySize );
1402         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1403         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404     }
1406     {
1407         InkAction* inky = ink_action_new( "NodeAutoAction",
1408                                           _("Node Auto"),
1409                                           _("Make selected nodes auto-smooth"),
1410                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1411                                           secondarySize );
1412         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1413         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1414     }
1416     {
1417         InkAction* inky = ink_action_new( "NodeLineAction",
1418                                           _("Node Line"),
1419                                           _("Make selected segments lines"),
1420                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1421                                           secondarySize );
1422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424     }
1426     {
1427         InkAction* inky = ink_action_new( "NodeCurveAction",
1428                                           _("Node Curve"),
1429                                           _("Make selected segments curves"),
1430                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1431                                           secondarySize );
1432         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1433         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1434     }
1436     {
1437         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1438                                                       _("Show Handles"),
1439                                                       _("Show the Bezier handles of selected nodes"),
1440                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1441                                                       Inkscape::ICON_SIZE_DECORATION );
1442         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1443         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1444         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1445     }
1447     {
1448         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1449                                                       _("Show Outline"),
1450                                                       _("Show the outline of the path"),
1451                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1452                                                       Inkscape::ICON_SIZE_DECORATION );
1453         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1454         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1455         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1456     }
1458     {
1459         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1460                                           _("Next path effect parameter"),
1461                                           _("Show next path effect parameter for editing"),
1462                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1463                                           Inkscape::ICON_SIZE_DECORATION );
1464         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1465         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1466         g_object_set_data( holder, "nodes_lpeedit", inky);
1467     }
1469     {
1470         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1471                                           _("Edit clipping path"),
1472                                           _("Edit the clipping path of the object"),
1473                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1474                                           Inkscape::ICON_SIZE_DECORATION );
1475         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1476         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1477         g_object_set_data( holder, "nodes_clippathedit", inky);
1478     }
1480     {
1481         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1482                                           _("Edit mask path"),
1483                                           _("Edit the mask of the object"),
1484                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1485                                           Inkscape::ICON_SIZE_DECORATION );
1486         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1487         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1488         g_object_set_data( holder, "nodes_maskedit", inky);
1489     }
1491     /* X coord of selected node(s) */
1492     {
1493         EgeAdjustmentAction* eact = 0;
1494         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1495         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1496         eact = create_adjustment_action( "NodeXAction",
1497                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1498                                          "/tools/nodes/Xcoord", 0,
1499                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1500                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1501                                          labels, values, G_N_ELEMENTS(labels),
1502                                          sp_node_path_x_value_changed );
1503         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1504         g_object_set_data( holder, "nodes_x_action", eact );
1505         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1506         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1507     }
1509     /* Y coord of selected node(s) */
1510     {
1511         EgeAdjustmentAction* eact = 0;
1512         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1513         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1514         eact = create_adjustment_action( "NodeYAction",
1515                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1516                                          "/tools/nodes/Ycoord", 0,
1517                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1518                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1519                                          labels, values, G_N_ELEMENTS(labels),
1520                                          sp_node_path_y_value_changed );
1521         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1522         g_object_set_data( holder, "nodes_y_action", eact );
1523         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1524         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1525     }
1527     // add the units menu
1528     {
1529         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1530         gtk_action_group_add_action( mainActions, act );
1531     }
1534     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1536     //watch selection
1537     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1539     sigc::connection *c_selection_changed =
1540         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1541                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1542     pool->add_connection ("selection-changed", c_selection_changed);
1544     sigc::connection *c_selection_modified =
1545         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1546                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1547     pool->add_connection ("selection-modified", c_selection_modified);
1549     sigc::connection *c_subselection_changed =
1550         new sigc::connection (desktop->connectToolSubselectionChanged
1551                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1552     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1554     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1556     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1557 } // end of sp_node_toolbox_prep()
1560 //########################
1561 //##    Zoom Toolbox    ##
1562 //########################
1564 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1566     // no custom GtkAction setup needed
1567 } // end of sp_zoom_toolbox_prep()
1569 void
1570 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1572     toolbox_set_desktop(toolbox,
1573                         desktop,
1574                         setup_tool_toolbox,
1575                         update_tool_toolbox,
1576                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1577                                                                          "event_context_connection")));
1581 void
1582 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1584     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1585                         desktop,
1586                         setup_aux_toolbox,
1587                         update_aux_toolbox,
1588                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1589                                                                          "event_context_connection")));
1592 void
1593 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1595     toolbox_set_desktop(toolbox,
1596                         desktop,
1597                         setup_commands_toolbox,
1598                         update_commands_toolbox,
1599                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1600                                                                          "event_context_connection")));
1603 void
1604 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1606     toolbox_set_desktop(toolbox,
1607                         desktop,
1608                         setup_snap_toolbox,
1609                         update_snap_toolbox,
1610                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1611                                                                          "event_context_connection")));
1615 static void
1616 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1618     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1619     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1621     if (old_desktop) {
1622         GList *children, *iter;
1624         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1625         for ( iter = children ; iter ; iter = iter->next ) {
1626             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1627         }
1628         g_list_free(children);
1629     }
1631     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1633     if (desktop) {
1634         gtk_widget_set_sensitive(toolbox, TRUE);
1635         setup_func(toolbox, desktop);
1636         update_func(desktop, desktop->event_context, toolbox);
1637         *conn = desktop->connectEventContextChanged
1638             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1639     } else {
1640         gtk_widget_set_sensitive(toolbox, FALSE);
1641     }
1643 } // end of toolbox_set_desktop()
1646 static void
1647 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1649     gchar const * descr =
1650         "<ui>"
1651         "  <toolbar name='ToolToolbar'>"
1652         "    <toolitem action='ToolSelector' />"
1653         "    <toolitem action='ToolNode' />"
1654         "    <toolitem action='ToolTweak' />"
1655         "    <toolitem action='ToolSpray' />"
1656         "    <toolitem action='ToolZoom' />"
1657         "    <toolitem action='ToolRect' />"
1658         "    <toolitem action='Tool3DBox' />"
1659         "    <toolitem action='ToolArc' />"
1660         "    <toolitem action='ToolStar' />"
1661         "    <toolitem action='ToolSpiral' />"
1662         "    <toolitem action='ToolPencil' />"
1663         "    <toolitem action='ToolPen' />"
1664         "    <toolitem action='ToolCalligraphic' />"
1665         "    <toolitem action='ToolEraser' />"
1666 //        "    <toolitem action='ToolLPETool' />"
1667         "    <toolitem action='ToolPaintBucket' />"
1668         "    <toolitem action='ToolText' />"
1669         "    <toolitem action='ToolConnector' />"
1670         "    <toolitem action='ToolGradient' />"
1671         "    <toolitem action='ToolDropper' />"
1672         "  </toolbar>"
1673         "</ui>";
1674     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1675     GtkUIManager* mgr = gtk_ui_manager_new();
1676     GError* errVal = 0;
1677     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1679     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1680     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1682     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1683     if ( prefs->getBool("/toolbox/icononly", true) ) {
1684         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1685     }
1686     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1687     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1689     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1690     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1692     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1694     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1695     if ( child ) {
1696         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1697     }
1699     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1700 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1704 static void
1705 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1707     gchar const *const tname = ( eventcontext
1708                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1709                                  : NULL );
1710     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1712     for (int i = 0 ; tools[i].type_name ; i++ ) {
1713         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1714         if ( act ) {
1715             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1716             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1717             if ( verbAct ) {
1718                 verbAct->set_active(setActive);
1719             }
1720         }
1721     }
1724 static void
1725 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1727     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1728     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1729     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1730     GtkUIManager* mgr = gtk_ui_manager_new();
1731     GError* errVal = 0;
1732     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1733     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1735     std::map<std::string, GtkWidget*> dataHolders;
1737     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1738         if ( aux_toolboxes[i].prep_func ) {
1739             // converted to GtkActions and UIManager
1741             GtkWidget* kludge = gtk_toolbar_new();
1742             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1743             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1744             dataHolders[aux_toolboxes[i].type_name] = kludge;
1745             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1746         } else {
1748             GtkWidget *sub_toolbox = 0;
1749             if (aux_toolboxes[i].create_func == NULL)
1750                 sub_toolbox = sp_empty_toolbox_new(desktop);
1751             else {
1752                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1753             }
1755             gtk_size_group_add_widget( grouper, sub_toolbox );
1757             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1758             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1760         }
1761     }
1763     // Second pass to create toolbars *after* all GtkActions are created
1764     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1765         if ( aux_toolboxes[i].prep_func ) {
1766             // converted to GtkActions and UIManager
1768             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1770             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1771             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1773             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1774             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1775             g_free( tmp );
1776             tmp = 0;
1778             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1779                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1780             }
1782             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1783             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1785             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1787             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1788                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1789                 swatch->setDesktop( desktop );
1790                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1791                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1792                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1793                 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 );
1794             }
1796             gtk_widget_show_all( holder );
1797             sp_set_font_size_smaller( holder );
1799             gtk_size_group_add_widget( grouper, holder );
1801             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1802             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1803         }
1804     }
1806     g_object_unref( G_OBJECT(grouper) );
1809 static void
1810 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1812     gchar const *tname = ( eventcontext
1813                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1814                            : NULL );
1815     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1816         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1817         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1818             gtk_widget_show_all(sub_toolbox);
1819             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1820         } else {
1821             gtk_widget_hide(sub_toolbox);
1822         }
1823     }
1826 static void
1827 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1829     gchar const * descr =
1830         "<ui>"
1831         "  <toolbar name='CommandsToolbar'>"
1832         "    <toolitem action='FileNew' />"
1833         "    <toolitem action='FileOpen' />"
1834         "    <toolitem action='FileSave' />"
1835         "    <toolitem action='FilePrint' />"
1836         "    <separator />"
1837         "    <toolitem action='FileImport' />"
1838         "    <toolitem action='FileExport' />"
1839         "    <separator />"
1840         "    <toolitem action='EditUndo' />"
1841         "    <toolitem action='EditRedo' />"
1842         "    <separator />"
1843         "    <toolitem action='EditCopy' />"
1844         "    <toolitem action='EditCut' />"
1845         "    <toolitem action='EditPaste' />"
1846         "    <separator />"
1847         "    <toolitem action='ZoomSelection' />"
1848         "    <toolitem action='ZoomDrawing' />"
1849         "    <toolitem action='ZoomPage' />"
1850         "    <separator />"
1851         "    <toolitem action='EditDuplicate' />"
1852         "    <toolitem action='EditClone' />"
1853         "    <toolitem action='EditUnlinkClone' />"
1854         "    <separator />"
1855         "    <toolitem action='SelectionGroup' />"
1856         "    <toolitem action='SelectionUnGroup' />"
1857         "    <separator />"
1858         "    <toolitem action='DialogFillStroke' />"
1859         "    <toolitem action='DialogText' />"
1860         "    <toolitem action='DialogLayers' />"
1861         "    <toolitem action='DialogXMLEditor' />"
1862         "    <toolitem action='DialogAlignDistribute' />"
1863         "    <separator />"
1864         "    <toolitem action='DialogPreferences' />"
1865         "    <toolitem action='DialogDocumentProperties' />"
1866         "  </toolbar>"
1867         "</ui>";
1868     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1869     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1871     GtkUIManager* mgr = gtk_ui_manager_new();
1872     GError* errVal = 0;
1874     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1875     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1877     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1878     if ( prefs->getBool("/toolbox/icononly", true) ) {
1879         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1880     }
1882     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1883     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1885     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1886     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1889     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1891     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1892     if ( child ) {
1893         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1894     }
1896     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1899 static void
1900 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1904 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1906     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1907         return;
1908     }
1910     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1911     g_assert(ptr != NULL);
1913     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1914     SPNamedView *nv = sp_desktop_namedview(dt);
1915     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1917     if (dt == NULL || nv == NULL) {
1918         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1919         return;
1920     }
1922     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1924     if (repr == NULL) {
1925         g_warning("This namedview doesn't have a xml representation attached!");
1926         return;
1927     }
1929     bool saved = sp_document_get_undo_sensitive(doc);
1930     sp_document_set_undo_sensitive(doc, false);
1932     bool v = false;
1933     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1935     switch (attr) {
1936         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1937             dt->toggleSnapGlobal();
1938             break;
1939         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1940             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1941             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1942             break;
1943         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1944             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1945             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1946             break;
1947         case SP_ATTR_INKSCAPE_BBOX_NODES:
1948             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1949             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1950             break;
1951         case SP_ATTR_INKSCAPE_SNAP_NODES:
1952             v = nv->snap_manager.snapprefs.getSnapModeNode();
1953             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1954             break;
1955         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1956             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1957             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1958             break;
1959         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1960             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1961             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1962             break;
1963         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1964             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1965             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1966             break;
1967         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1968             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1969             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1970             break;
1971         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1972             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1973             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1974             break;
1975         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1976             v = nv->snap_manager.snapprefs.getSnapToGrids();
1977             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1978             break;
1979         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1980             v = nv->snap_manager.snapprefs.getSnapToGuides();
1981             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1982             break;
1983         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1984             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1985             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1986             break;
1987             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1988               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1989               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1990               break;*/
1991         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1992             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1993             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1994             break;
1995         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1996             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1997             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1998             break;
1999         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2000             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2001             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2002             break;
2003         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2004             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2005             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2006             break;
2007         default:
2008             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2009             break;
2010     }
2012     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2013     doc->setModifiedSinceSave();
2015     sp_document_set_undo_sensitive(doc, saved);
2018 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2020     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2021     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2023     gchar const * descr =
2024         "<ui>"
2025         "  <toolbar name='SnapToolbar'>"
2026         "    <toolitem action='ToggleSnapGlobal' />"
2027         "    <separator />"
2028         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2029         "    <toolitem action='ToggleSnapToBBoxPath' />"
2030         "    <toolitem action='ToggleSnapToBBoxNode' />"
2031         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2032         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2033         "    <separator />"
2034         "    <toolitem action='ToggleSnapFromNode' />"
2035         "    <toolitem action='ToggleSnapToItemPath' />"
2036         "    <toolitem action='ToggleSnapToPathIntersections' />"
2037         "    <toolitem action='ToggleSnapToItemNode' />"
2038         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2039         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2040         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2041         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2042         "    <separator />"
2043         "    <toolitem action='ToggleSnapToPageBorder' />"
2044         "    <toolitem action='ToggleSnapToGrids' />"
2045         "    <toolitem action='ToggleSnapToGuides' />"
2046         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2047         "  </toolbar>"
2048         "</ui>";
2050     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2052     {
2053         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2054                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2055                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2057         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2058         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2059     }
2061     {
2062         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2063                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2064                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2066         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2067         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2068     }
2070     {
2071         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2072                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2073                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2075         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2076         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2077     }
2079     {
2080         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2081                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2082                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2084         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2085         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2086     }
2088     {
2089         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2090                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2091                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2092                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2094         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2095         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2096     }
2098     {
2099         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2100                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2101                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2103         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2104         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2105     }
2107     {
2108         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2109                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2111         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2112         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2113     }
2115     {
2116         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2117                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2118                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2120         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2121         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2122     }
2124     {
2125         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2126                                                      _("Path intersections"), _("Snap to path intersections"),
2127                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2129         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2130         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2131     }
2133     {
2134         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2135                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2136                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2138         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2139         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2140     }
2142     {
2143         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2144                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2145                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2147         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2148         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2149     }
2151     {
2152         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2153                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2154                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2156         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2157         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2158     }
2160     {
2161         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2162                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2163                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2165         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2166         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2167     }
2169     {
2170         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2171                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2172                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2174         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2175         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2176     }
2178     {
2179         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2180                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2181                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2183         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2184         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2185     }
2187     {
2188         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2189                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2190                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2192         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2193         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2194     }
2196     {
2197         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2198                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2199                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2201         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2202         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2203     }
2205     /*{
2206       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2207       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2208       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2209       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2211       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2212       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2213       }*/
2215     GtkUIManager* mgr = gtk_ui_manager_new();
2216     GError* errVal = 0;
2218     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2219     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2221     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2222     if ( prefs->getBool("/toolbox/icononly", true) ) {
2223         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2224     }
2226     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2227     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2229     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2230     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2232     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2234     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2235     if ( child ) {
2236         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2237     }
2239     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2243 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2245     g_assert(desktop != NULL);
2246     g_assert(toolbox != NULL);
2248     SPNamedView *nv = sp_desktop_namedview(desktop);
2249     if (nv == NULL) {
2250         g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2251         return;
2252     }
2254     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2256     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2257     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2258     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2259     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2260     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2261     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2262     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2263     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2264     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2265     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2266     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2267     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2268     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2269     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2270     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2271     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2272     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2273     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2276     if (!act1) {
2277         return; // The snap actions haven't been defined yet (might be the case during startup)
2278     }
2280     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2281     // changes in our document because we're only updating the UI;
2282     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2283     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2285     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2286     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2288     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2289     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2290     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2292     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2293     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2294     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2295     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2296     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2297     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2298     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2299     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2301     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2302     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2303     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2305     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2306     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2307     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2308     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2309     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2310     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2311     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2312     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2313     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2314     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2315     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2316     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2317     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2318     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2319     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2321     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2322     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2323     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2324     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2326     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2327     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2328     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2329     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2332     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2335 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2337     gtk_widget_show(toolbox_toplevel);
2338     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2340     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2341     if (!shown_toolbox) {
2342         return;
2343     }
2344     gtk_widget_show(toolbox);
2346     gtk_widget_show_all(shown_toolbox);
2349 static GtkWidget *
2350 sp_empty_toolbox_new(SPDesktop *desktop)
2352     GtkWidget *tbl = gtk_toolbar_new();
2353     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2354     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2356     gtk_widget_show_all(tbl);
2357     sp_set_font_size_smaller (tbl);
2359     return tbl;
2362 #define MODE_LABEL_WIDTH 70
2364 //########################
2365 //##       Star         ##
2366 //########################
2368 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2370     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2372     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2373         // do not remember prefs if this call is initiated by an undo change, because undoing object
2374         // creation sets bogus values to its attributes before it is deleted
2375         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2376         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2377     }
2379     // quit if run by the attr_changed listener
2380     if (g_object_get_data( dataKludge, "freeze" )) {
2381         return;
2382     }
2384     // in turn, prevent listener from responding
2385     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2387     bool modmade = false;
2389     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2390     GSList const *items = selection->itemList();
2391     for (; items != NULL; items = items->next) {
2392         if (SP_IS_STAR((SPItem *) items->data)) {
2393             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2394             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2395             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2396                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2397                                     + M_PI / (gint)adj->value));
2398             SP_OBJECT((SPItem *) items->data)->updateRepr();
2399             modmade = true;
2400         }
2401     }
2402     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2403                                    _("Star: Change number of corners"));
2405     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2408 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2410     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2412     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2413         if (!IS_NAN(adj->value)) {
2414             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2415             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2416         }
2417     }
2419     // quit if run by the attr_changed listener
2420     if (g_object_get_data( dataKludge, "freeze" )) {
2421         return;
2422     }
2424     // in turn, prevent listener from responding
2425     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2427     bool modmade = false;
2428     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2429     GSList const *items = selection->itemList();
2430     for (; items != NULL; items = items->next) {
2431         if (SP_IS_STAR((SPItem *) items->data)) {
2432             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2434             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2435             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2436             if (r2 < r1) {
2437                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2438             } else {
2439                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2440             }
2442             SP_OBJECT((SPItem *) items->data)->updateRepr();
2443             modmade = true;
2444         }
2445     }
2447     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2448                                    _("Star: Change spoke ratio"));
2450     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2453 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2455     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2456     bool flat = ege_select_one_action_get_active( act ) == 0;
2458     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2459         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2460         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2461     }
2463     // quit if run by the attr_changed listener
2464     if (g_object_get_data( dataKludge, "freeze" )) {
2465         return;
2466     }
2468     // in turn, prevent listener from responding
2469     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2471     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2472     GSList const *items = selection->itemList();
2473     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2474     bool modmade = false;
2476     if ( prop_action ) {
2477         gtk_action_set_sensitive( prop_action, !flat );
2478     }
2480     for (; items != NULL; items = items->next) {
2481         if (SP_IS_STAR((SPItem *) items->data)) {
2482             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2483             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2484             SP_OBJECT((SPItem *) items->data)->updateRepr();
2485             modmade = true;
2486         }
2487     }
2489     if (modmade) {
2490         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2491                          flat ? _("Make polygon") : _("Make star"));
2492     }
2494     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2497 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2499     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2501     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2502         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2503         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2504     }
2506     // quit if run by the attr_changed listener
2507     if (g_object_get_data( dataKludge, "freeze" )) {
2508         return;
2509     }
2511     // in turn, prevent listener from responding
2512     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2514     bool modmade = false;
2516     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2517     GSList const *items = selection->itemList();
2518     for (; items != NULL; items = items->next) {
2519         if (SP_IS_STAR((SPItem *) items->data)) {
2520             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2521             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2522             SP_OBJECT(items->data)->updateRepr();
2523             modmade = true;
2524         }
2525     }
2526     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2527                                    _("Star: Change rounding"));
2529     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2532 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2534     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2536     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2537         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2538         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2539     }
2541     // quit if run by the attr_changed listener
2542     if (g_object_get_data( dataKludge, "freeze" )) {
2543         return;
2544     }
2546     // in turn, prevent listener from responding
2547     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2549     bool modmade = false;
2551     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2552     GSList const *items = selection->itemList();
2553     for (; items != NULL; items = items->next) {
2554         if (SP_IS_STAR((SPItem *) items->data)) {
2555             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2556             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2557             SP_OBJECT(items->data)->updateRepr();
2558             modmade = true;
2559         }
2560     }
2561     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2562                                    _("Star: Change randomization"));
2564     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2568 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2569                                        gchar const */*old_value*/, gchar const */*new_value*/,
2570                                        bool /*is_interactive*/, gpointer data)
2572     GtkWidget *tbl = GTK_WIDGET(data);
2574     // quit if run by the _changed callbacks
2575     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2576         return;
2577     }
2579     // in turn, prevent callbacks from responding
2580     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2582     GtkAdjustment *adj = 0;
2584     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2585     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2587     if (!strcmp(name, "inkscape:randomized")) {
2588         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2589         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2590     } else if (!strcmp(name, "inkscape:rounded")) {
2591         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2592         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2593     } else if (!strcmp(name, "inkscape:flatsided")) {
2594         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2595         char const *flatsides = repr->attribute("inkscape:flatsided");
2596         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2597         if ( flatsides && !strcmp(flatsides,"false") ) {
2598             ege_select_one_action_set_active( flat_action, 1 );
2599             gtk_action_set_sensitive( prop_action, TRUE );
2600         } else {
2601             ege_select_one_action_set_active( flat_action, 0 );
2602             gtk_action_set_sensitive( prop_action, FALSE );
2603         }
2604     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2605         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2606         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2607         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2608         if (r2 < r1) {
2609             gtk_adjustment_set_value(adj, r2/r1);
2610         } else {
2611             gtk_adjustment_set_value(adj, r1/r2);
2612         }
2613     } else if (!strcmp(name, "sodipodi:sides")) {
2614         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2615         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2616     }
2618     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2622 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2624     NULL, /* child_added */
2625     NULL, /* child_removed */
2626     star_tb_event_attr_changed,
2627     NULL, /* content_changed */
2628     NULL  /* order_changed */
2629 };
2632 /**
2633  *  \param selection Should not be NULL.
2634  */
2635 static void
2636 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2638     int n_selected = 0;
2639     Inkscape::XML::Node *repr = NULL;
2641     purge_repr_listener( tbl, tbl );
2643     for (GSList const *items = selection->itemList();
2644          items != NULL;
2645          items = items->next)
2646     {
2647         if (SP_IS_STAR((SPItem *) items->data)) {
2648             n_selected++;
2649             repr = SP_OBJECT_REPR((SPItem *) items->data);
2650         }
2651     }
2653     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2655     if (n_selected == 0) {
2656         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2657     } else if (n_selected == 1) {
2658         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2660         if (repr) {
2661             g_object_set_data( tbl, "repr", repr );
2662             Inkscape::GC::anchor(repr);
2663             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2664             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2665         }
2666     } else {
2667         // FIXME: implement averaging of all parameters for multiple selected stars
2668         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2669         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2670     }
2674 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2676     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2677     // callbacks to lump all the changes for all selected objects in one undo step
2679     GtkAdjustment *adj = 0;
2681     // fixme: make settable in prefs!
2682     gint mag = 5;
2683     gdouble prop = 0.5;
2684     gboolean flat = FALSE;
2685     gdouble randomized = 0;
2686     gdouble rounded = 0;
2688     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2689     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2691     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2692     gtk_action_set_sensitive( sb2, !flat );
2694     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2695     gtk_adjustment_set_value(adj, mag);
2696     gtk_adjustment_value_changed(adj);
2698     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2699     gtk_adjustment_set_value(adj, prop);
2700     gtk_adjustment_value_changed(adj);
2702     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2703     gtk_adjustment_set_value(adj, rounded);
2704     gtk_adjustment_value_changed(adj);
2706     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2707     gtk_adjustment_set_value(adj, randomized);
2708     gtk_adjustment_value_changed(adj);
2712 void
2713 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2715     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2716     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2717     GtkWidget *l = gtk_label_new(NULL);
2718     gtk_label_set_markup(GTK_LABEL(l), title);
2719     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2720     if ( GTK_IS_TOOLBAR(tbl) ) {
2721         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2722     } else {
2723         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2724     }
2725     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2729 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2731     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2733     {
2734         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2735         ege_output_action_set_use_markup( act, TRUE );
2736         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2737         g_object_set_data( holder, "mode_action", act );
2738     }
2740     {
2741         EgeAdjustmentAction* eact = 0;
2742         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2743         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2745         /* Flatsided checkbox */
2746         {
2747             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2749             GtkTreeIter iter;
2750             gtk_list_store_append( model, &iter );
2751             gtk_list_store_set( model, &iter,
2752                                 0, _("Polygon"),
2753                                 1, _("Regular polygon (with one handle) instead of a star"),
2754                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2755                                 -1 );
2757             gtk_list_store_append( model, &iter );
2758             gtk_list_store_set( model, &iter,
2759                                 0, _("Star"),
2760                                 1, _("Star instead of a regular polygon (with one handle)"),
2761                                 2, INKSCAPE_ICON_DRAW_STAR,
2762                                 -1 );
2764             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2765             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2766             g_object_set_data( holder, "flat_action", act );
2768             ege_select_one_action_set_appearance( act, "full" );
2769             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2770             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2771             ege_select_one_action_set_icon_column( act, 2 );
2772             ege_select_one_action_set_icon_size( act, secondarySize );
2773             ege_select_one_action_set_tooltip_column( act, 1  );
2775             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2776             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2777         }
2779         /* Magnitude */
2780         {
2781         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2782         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2783         eact = create_adjustment_action( "MagnitudeAction",
2784                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2785                                          "/tools/shapes/star/magnitude", 3,
2786                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2787                                          3, 1024, 1, 5,
2788                                          labels, values, G_N_ELEMENTS(labels),
2789                                          sp_stb_magnitude_value_changed,
2790                                          1.0, 0 );
2791         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2792         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2793         }
2795         /* Spoke ratio */
2796         {
2797         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2798         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2799         eact = create_adjustment_action( "SpokeAction",
2800                                          _("Spoke ratio"), _("Spoke ratio:"),
2801                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2802                                          // Base radius is the same for the closest handle.
2803                                          _("Base radius to tip radius ratio"),
2804                                          "/tools/shapes/star/proportion", 0.5,
2805                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2806                                          0.01, 1.0, 0.01, 0.1,
2807                                          labels, values, G_N_ELEMENTS(labels),
2808                                          sp_stb_proportion_value_changed );
2809         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810         g_object_set_data( holder, "prop_action", eact );
2811         }
2813         if ( !isFlatSided ) {
2814             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2815         } else {
2816             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2817         }
2819         /* Roundedness */
2820         {
2821         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2822         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2823         eact = create_adjustment_action( "RoundednessAction",
2824                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2825                                          "/tools/shapes/star/rounded", 0.0,
2826                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2827                                          -10.0, 10.0, 0.01, 0.1,
2828                                          labels, values, G_N_ELEMENTS(labels),
2829                                          sp_stb_rounded_value_changed );
2830         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2831         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2832         }
2834         /* Randomization */
2835         {
2836         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2837         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2838         eact = create_adjustment_action( "RandomizationAction",
2839                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2840                                          "/tools/shapes/star/randomized", 0.0,
2841                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2842                                          -10.0, 10.0, 0.001, 0.01,
2843                                          labels, values, G_N_ELEMENTS(labels),
2844                                          sp_stb_randomized_value_changed, 0.1, 3 );
2845         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2846         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2847         }
2848     }
2850     {
2851         /* Reset */
2852         {
2853             GtkAction* act = gtk_action_new( "StarResetAction",
2854                                              _("Defaults"),
2855                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2856                                              GTK_STOCK_CLEAR );
2857             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2858             gtk_action_group_add_action( mainActions, act );
2859             gtk_action_set_sensitive( act, TRUE );
2860         }
2861     }
2863     sigc::connection *connection = new sigc::connection(
2864         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2865         );
2866     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2867     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2871 //########################
2872 //##       Rect         ##
2873 //########################
2875 static void sp_rtb_sensitivize( GObject *tbl )
2877     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2878     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2879     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2881     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2882         gtk_action_set_sensitive( not_rounded, FALSE );
2883     } else {
2884         gtk_action_set_sensitive( not_rounded, TRUE );
2885     }
2889 static void
2890 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2891                           void (*setter)(SPRect *, gdouble))
2893     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2895     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2896     SPUnit const *unit = tracker->getActiveUnit();
2898     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2899         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2900         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2901     }
2903     // quit if run by the attr_changed listener
2904     if (g_object_get_data( tbl, "freeze" )) {
2905         return;
2906     }
2908     // in turn, prevent listener from responding
2909     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2911     bool modmade = false;
2912     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2913     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2914         if (SP_IS_RECT(items->data)) {
2915             if (adj->value != 0) {
2916                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2917             } else {
2918                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2919             }
2920             modmade = true;
2921         }
2922     }
2924     sp_rtb_sensitivize( tbl );
2926     if (modmade) {
2927         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2928                                    _("Change rectangle"));
2929     }
2931     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2934 static void
2935 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2937     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2940 static void
2941 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2943     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2946 static void
2947 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2949     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2952 static void
2953 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2955     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2960 static void
2961 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2963     GtkAdjustment *adj = 0;
2965     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2966     gtk_adjustment_set_value(adj, 0.0);
2967     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2968     gtk_adjustment_value_changed(adj);
2970     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2971     gtk_adjustment_set_value(adj, 0.0);
2972     gtk_adjustment_value_changed(adj);
2974     sp_rtb_sensitivize( obj );
2977 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2978                                        gchar const */*old_value*/, gchar const */*new_value*/,
2979                                        bool /*is_interactive*/, gpointer data)
2981     GObject *tbl = G_OBJECT(data);
2983     // quit if run by the _changed callbacks
2984     if (g_object_get_data( tbl, "freeze" )) {
2985         return;
2986     }
2988     // in turn, prevent callbacks from responding
2989     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2991     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2992     SPUnit const *unit = tracker->getActiveUnit();
2994     gpointer item = g_object_get_data( tbl, "item" );
2995     if (item && SP_IS_RECT(item)) {
2996         {
2997             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2998             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2999             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3000         }
3002         {
3003             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3004             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3005             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3006         }
3008         {
3009             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3010             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3011             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3012         }
3014         {
3015             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3016             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3017             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3018         }
3019     }
3021     sp_rtb_sensitivize( tbl );
3023     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3027 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3028     NULL, /* child_added */
3029     NULL, /* child_removed */
3030     rect_tb_event_attr_changed,
3031     NULL, /* content_changed */
3032     NULL  /* order_changed */
3033 };
3035 /**
3036  *  \param selection should not be NULL.
3037  */
3038 static void
3039 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3041     int n_selected = 0;
3042     Inkscape::XML::Node *repr = NULL;
3043     SPItem *item = NULL;
3045     if ( g_object_get_data( tbl, "repr" ) ) {
3046         g_object_set_data( tbl, "item", NULL );
3047     }
3048     purge_repr_listener( tbl, tbl );
3050     for (GSList const *items = selection->itemList();
3051          items != NULL;
3052          items = items->next) {
3053         if (SP_IS_RECT((SPItem *) items->data)) {
3054             n_selected++;
3055             item = (SPItem *) items->data;
3056             repr = SP_OBJECT_REPR(item);
3057         }
3058     }
3060     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3062     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3064     if (n_selected == 0) {
3065         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3067         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3068         gtk_action_set_sensitive(w, FALSE);
3069         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3070         gtk_action_set_sensitive(h, FALSE);
3072     } else if (n_selected == 1) {
3073         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3074         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3076         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3077         gtk_action_set_sensitive(w, TRUE);
3078         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3079         gtk_action_set_sensitive(h, TRUE);
3081         if (repr) {
3082             g_object_set_data( tbl, "repr", repr );
3083             g_object_set_data( tbl, "item", item );
3084             Inkscape::GC::anchor(repr);
3085             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3086             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3087         }
3088     } else {
3089         // FIXME: implement averaging of all parameters for multiple selected
3090         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3091         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3092         sp_rtb_sensitivize( tbl );
3093     }
3097 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3099     EgeAdjustmentAction* eact = 0;
3100     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3102     {
3103         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3104         ege_output_action_set_use_markup( act, TRUE );
3105         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3106         g_object_set_data( holder, "mode_action", act );
3107     }
3109     // rx/ry units menu: create
3110     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3111     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3112     // fixme: add % meaning per cent of the width/height
3113     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3114     g_object_set_data( holder, "tracker", tracker );
3116     /* W */
3117     {
3118         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3119         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3120         eact = create_adjustment_action( "RectWidthAction",
3121                                          _("Width"), _("W:"), _("Width of rectangle"),
3122                                          "/tools/shapes/rect/width", 0,
3123                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3124                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3125                                          labels, values, G_N_ELEMENTS(labels),
3126                                          sp_rtb_width_value_changed );
3127         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3128         g_object_set_data( holder, "width_action", eact );
3129         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3130         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3131     }
3133     /* H */
3134     {
3135         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3136         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3137         eact = create_adjustment_action( "RectHeightAction",
3138                                          _("Height"), _("H:"), _("Height of rectangle"),
3139                                          "/tools/shapes/rect/height", 0,
3140                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3141                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3142                                          labels, values, G_N_ELEMENTS(labels),
3143                                          sp_rtb_height_value_changed );
3144         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3145         g_object_set_data( holder, "height_action", eact );
3146         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3147         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3148     }
3150     /* rx */
3151     {
3152         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3153         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3154         eact = create_adjustment_action( "RadiusXAction",
3155                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3156                                          "/tools/shapes/rect/rx", 0,
3157                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3158                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3159                                          labels, values, G_N_ELEMENTS(labels),
3160                                          sp_rtb_rx_value_changed);
3161         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3162         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3163     }
3165     /* ry */
3166     {
3167         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3168         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3169         eact = create_adjustment_action( "RadiusYAction",
3170                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3171                                          "/tools/shapes/rect/ry", 0,
3172                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3173                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3174                                          labels, values, G_N_ELEMENTS(labels),
3175                                          sp_rtb_ry_value_changed);
3176         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3177         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3178     }
3180     // add the units menu
3181     {
3182         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3183         gtk_action_group_add_action( mainActions, act );
3184     }
3186     /* Reset */
3187     {
3188         InkAction* inky = ink_action_new( "RectResetAction",
3189                                           _("Not rounded"),
3190                                           _("Make corners sharp"),
3191                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3192                                           secondarySize );
3193         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3194         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3195         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3196         g_object_set_data( holder, "not_rounded", inky );
3197     }
3199     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3200     sp_rtb_sensitivize( holder );
3202     sigc::connection *connection = new sigc::connection(
3203         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3204         );
3205     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3206     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3209 //########################
3210 //##       3D Box       ##
3211 //########################
3213 // normalize angle so that it lies in the interval [0,360]
3214 static double box3d_normalize_angle (double a) {
3215     double angle = a + ((int) (a/360.0))*360;
3216     if (angle < 0) {
3217         angle += 360.0;
3218     }
3219     return angle;
3222 static void
3223 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3224                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3225     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3226     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3227     //       are reset).
3228     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3230     if (is_infinite) {
3231         gtk_toggle_action_set_active(tact, TRUE);
3232         gtk_action_set_sensitive(act, TRUE);
3234         double angle = persp3d_get_infinite_angle(persp, axis);
3235         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3236             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3237         }
3238     } else {
3239         gtk_toggle_action_set_active(tact, FALSE);
3240         gtk_action_set_sensitive(act, FALSE);
3241     }
3244 static void
3245 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3246     if (!persp_repr) {
3247         g_print ("No perspective given to box3d_resync_toolbar().\n");
3248         return;
3249     }
3251     GtkWidget *tbl = GTK_WIDGET(data);
3252     GtkAdjustment *adj = 0;
3253     GtkAction *act = 0;
3254     GtkToggleAction *tact = 0;
3255     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3256     if (!persp) {
3257         // Hmm, is it an error if this happens?
3258         return;
3259     }
3260     {
3261         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3262         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3263         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3265         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3266     }
3267     {
3268         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3269         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3270         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3272         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3273     }
3274     {
3275         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3276         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3277         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3279         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3280     }
3283 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3284                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3285                                                   bool /*is_interactive*/, gpointer data)
3287     GtkWidget *tbl = GTK_WIDGET(data);
3289     // quit if run by the attr_changed or selection changed listener
3290     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3291         return;
3292     }
3294     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3295     // sp_document_maybe_done() when the document is undo insensitive)
3296     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3298     // TODO: Only update the appropriate part of the toolbar
3299 //    if (!strcmp(name, "inkscape:vp_z")) {
3300         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3301 //    }
3303     Persp3D *persp = persp3d_get_from_repr(repr);
3304     persp3d_update_box_reprs(persp);
3306     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3309 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3311     NULL, /* child_added */
3312     NULL, /* child_removed */
3313     box3d_persp_tb_event_attr_changed,
3314     NULL, /* content_changed */
3315     NULL  /* order_changed */
3316 };
3318 /**
3319  *  \param selection Should not be NULL.
3320  */
3321 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3322 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3323 static void
3324 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3326     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3327     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3328     // update the perspectives with infinite VPs and leave the other ones untouched).
3330     Inkscape::XML::Node *persp_repr = NULL;
3331     purge_repr_listener(tbl, tbl);
3333     SPItem *item = selection->singleItem();
3334     if (item && SP_IS_BOX3D(item)) {
3335         // FIXME: Also deal with multiple selected boxes
3336         SPBox3D *box = SP_BOX3D(item);
3337         Persp3D *persp = box3d_get_perspective(box);
3338         persp_repr = SP_OBJECT_REPR(persp);
3339         if (persp_repr) {
3340             g_object_set_data(tbl, "repr", persp_repr);
3341             Inkscape::GC::anchor(persp_repr);
3342             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3343             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3344         }
3346         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3347         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3348         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3350         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3351         box3d_resync_toolbar(persp_repr, tbl);
3352         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3353     }
3356 static void
3357 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3359     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3360     SPDocument *document = sp_desktop_document(desktop);
3362     // quit if run by the attr_changed or selection changed listener
3363     if (g_object_get_data( dataKludge, "freeze" )) {
3364         return;
3365     }
3367     // in turn, prevent listener from responding
3368     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3370     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3371     if (sel_persps.empty()) {
3372         // this can happen when the document is created; we silently ignore it
3373         return;
3374     }
3375     Persp3D *persp = sel_persps.front();
3377     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3378     SP_OBJECT(persp)->updateRepr();
3380     // TODO: use the correct axis here, too
3381     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3383     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3387 static void
3388 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3390     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3393 static void
3394 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3396     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3399 static void
3400 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3402     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3406 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3408     // TODO: Take all selected perspectives into account
3409     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3410     if (sel_persps.empty()) {
3411         // this can happen when the document is created; we silently ignore it
3412         return;
3413     }
3414     Persp3D *persp = sel_persps.front();
3416     bool set_infinite = gtk_toggle_action_get_active(act);
3417     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3420 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3422     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3425 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3427     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3430 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3432     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3435 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3437     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3438     EgeAdjustmentAction* eact = 0;
3439     SPDocument *document = sp_desktop_document (desktop);
3440     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3442     EgeAdjustmentAction* box3d_angle_x = 0;
3443     EgeAdjustmentAction* box3d_angle_y = 0;
3444     EgeAdjustmentAction* box3d_angle_z = 0;
3446     /* Angle X */
3447     {
3448         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3449         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3450         eact = create_adjustment_action( "3DBoxAngleXAction",
3451                                          _("Angle in X direction"), _("Angle X:"),
3452                                          // Translators: PL is short for 'perspective line'
3453                                          _("Angle of PLs in X direction"),
3454                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3455                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3456                                          -360.0, 360.0, 1.0, 10.0,
3457                                          labels, values, G_N_ELEMENTS(labels),
3458                                          box3d_angle_x_value_changed );
3459         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3460         g_object_set_data( holder, "box3d_angle_x_action", eact );
3461         box3d_angle_x = eact;
3462     }
3464     if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3465         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3466     } else {
3467         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3468     }
3471     /* VP X state */
3472     {
3473         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3474                                                       // Translators: VP is short for 'vanishing point'
3475                                                       _("State of VP in X direction"),
3476                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3477                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3478                                                       Inkscape::ICON_SIZE_DECORATION );
3479         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3480         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3481         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3482         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3483         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3484     }
3486     /* Angle Y */
3487     {
3488         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3489         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3490         eact = create_adjustment_action( "3DBoxAngleYAction",
3491                                          _("Angle in Y direction"), _("Angle Y:"),
3492                                          // Translators: PL is short for 'perspective line'
3493                                          _("Angle of PLs in Y direction"),
3494                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3495                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3496                                          -360.0, 360.0, 1.0, 10.0,
3497                                          labels, values, G_N_ELEMENTS(labels),
3498                                          box3d_angle_y_value_changed );
3499         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3500         g_object_set_data( holder, "box3d_angle_y_action", eact );
3501         box3d_angle_y = eact;
3502     }
3504     if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3505         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3506     } else {
3507         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3508     }
3510     /* VP Y state */
3511     {
3512         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3513                                                       // Translators: VP is short for 'vanishing point'
3514                                                       _("State of VP in Y direction"),
3515                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3516                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3517                                                       Inkscape::ICON_SIZE_DECORATION );
3518         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3519         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3520         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3521         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3522         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3523     }
3525     /* Angle Z */
3526     {
3527         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3528         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3529         eact = create_adjustment_action( "3DBoxAngleZAction",
3530                                          _("Angle in Z direction"), _("Angle Z:"),
3531                                          // Translators: PL is short for 'perspective line'
3532                                          _("Angle of PLs in Z direction"),
3533                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3534                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3535                                          -360.0, 360.0, 1.0, 10.0,
3536                                          labels, values, G_N_ELEMENTS(labels),
3537                                          box3d_angle_z_value_changed );
3538         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3539         g_object_set_data( holder, "box3d_angle_z_action", eact );
3540         box3d_angle_z = eact;
3541     }
3543     if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3544         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3545     } else {
3546         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3547     }
3549     /* VP Z state */
3550     {
3551         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3552                                                       // Translators: VP is short for 'vanishing point'
3553                                                       _("State of VP in Z direction"),
3554                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3555                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3556                                                       Inkscape::ICON_SIZE_DECORATION );
3557         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3558         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3559         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3560         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3561         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3562     }
3564     sigc::connection *connection = new sigc::connection(
3565         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3566        );
3567     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3568     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3571 //########################
3572 //##       Spiral       ##
3573 //########################
3575 static void
3576 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3578     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3580     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3581         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3582         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3583     }
3585     // quit if run by the attr_changed listener
3586     if (g_object_get_data( tbl, "freeze" )) {
3587         return;
3588     }
3590     // in turn, prevent listener from responding
3591     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3593     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3595     bool modmade = false;
3596     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3597          items != NULL;
3598          items = items->next)
3599     {
3600         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3601             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3602             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3603             SP_OBJECT((SPItem *) items->data)->updateRepr();
3604             modmade = true;
3605         }
3606     }
3608     g_free(namespaced_name);
3610     if (modmade) {
3611         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3612                                    _("Change spiral"));
3613     }
3615     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3618 static void
3619 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3621     sp_spl_tb_value_changed(adj, tbl, "revolution");
3624 static void
3625 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3627     sp_spl_tb_value_changed(adj, tbl, "expansion");
3630 static void
3631 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3633     sp_spl_tb_value_changed(adj, tbl, "t0");
3636 static void
3637 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3639     GtkWidget *tbl = GTK_WIDGET(obj);
3641     GtkAdjustment *adj;
3643     // fixme: make settable
3644     gdouble rev = 5;
3645     gdouble exp = 1.0;
3646     gdouble t0 = 0.0;
3648     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3649     gtk_adjustment_set_value(adj, rev);
3650     gtk_adjustment_value_changed(adj);
3652     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3653     gtk_adjustment_set_value(adj, exp);
3654     gtk_adjustment_value_changed(adj);
3656     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3657     gtk_adjustment_set_value(adj, t0);
3658     gtk_adjustment_value_changed(adj);
3660     spinbutton_defocus(GTK_OBJECT(tbl));
3664 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3665                                          gchar const */*old_value*/, gchar const */*new_value*/,
3666                                          bool /*is_interactive*/, gpointer data)
3668     GtkWidget *tbl = GTK_WIDGET(data);
3670     // quit if run by the _changed callbacks
3671     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3672         return;
3673     }
3675     // in turn, prevent callbacks from responding
3676     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3678     GtkAdjustment *adj;
3679     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3680     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3682     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3683     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3685     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3686     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3688     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3692 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3693     NULL, /* child_added */
3694     NULL, /* child_removed */
3695     spiral_tb_event_attr_changed,
3696     NULL, /* content_changed */
3697     NULL  /* order_changed */
3698 };
3700 static void
3701 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3703     int n_selected = 0;
3704     Inkscape::XML::Node *repr = NULL;
3706     purge_repr_listener( tbl, tbl );
3708     for (GSList const *items = selection->itemList();
3709          items != NULL;
3710          items = items->next)
3711     {
3712         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3713             n_selected++;
3714             repr = SP_OBJECT_REPR((SPItem *) items->data);
3715         }
3716     }
3718     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3720     if (n_selected == 0) {
3721         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3722     } else if (n_selected == 1) {
3723         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3725         if (repr) {
3726             g_object_set_data( tbl, "repr", repr );
3727             Inkscape::GC::anchor(repr);
3728             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3729             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3730         }
3731     } else {
3732         // FIXME: implement averaging of all parameters for multiple selected
3733         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3734         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3735     }
3739 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3741     EgeAdjustmentAction* eact = 0;
3742     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3744     {
3745         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3746         ege_output_action_set_use_markup( act, TRUE );
3747         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748         g_object_set_data( holder, "mode_action", act );
3749     }
3751     /* Revolution */
3752     {
3753         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3754         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3755         eact = create_adjustment_action( "SpiralRevolutionAction",
3756                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3757                                          "/tools/shapes/spiral/revolution", 3.0,
3758                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3759                                          0.01, 1024.0, 0.1, 1.0,
3760                                          labels, values, G_N_ELEMENTS(labels),
3761                                          sp_spl_tb_revolution_value_changed, 1, 2);
3762         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3763     }
3765     /* Expansion */
3766     {
3767         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3768         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3769         eact = create_adjustment_action( "SpiralExpansionAction",
3770                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3771                                          "/tools/shapes/spiral/expansion", 1.0,
3772                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3773                                          0.0, 1000.0, 0.01, 1.0,
3774                                          labels, values, G_N_ELEMENTS(labels),
3775                                          sp_spl_tb_expansion_value_changed);
3776         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3777     }
3779     /* T0 */
3780     {
3781         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3782         gdouble values[] = {0, 0.5, 0.9};
3783         eact = create_adjustment_action( "SpiralT0Action",
3784                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3785                                          "/tools/shapes/spiral/t0", 0.0,
3786                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3787                                          0.0, 0.999, 0.01, 1.0,
3788                                          labels, values, G_N_ELEMENTS(labels),
3789                                          sp_spl_tb_t0_value_changed);
3790         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3791     }
3793     /* Reset */
3794     {
3795         InkAction* inky = ink_action_new( "SpiralResetAction",
3796                                           _("Defaults"),
3797                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3798                                           GTK_STOCK_CLEAR,
3799                                           secondarySize );
3800         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3801         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3802     }
3805     sigc::connection *connection = new sigc::connection(
3806         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3807         );
3808     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3809     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3812 //########################
3813 //##     Pen/Pencil     ##
3814 //########################
3816 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3817 static Glib::ustring const
3818 freehand_tool_name(GObject *dataKludge)
3820     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3821     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3822              ? "/tools/freehand/pen"
3823              : "/tools/freehand/pencil" );
3826 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3828     gint mode = ege_select_one_action_get_active(act);
3830     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3831     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3833     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3835     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3836     // preparatory work here
3837     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3838         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3839         sp_pen_context_set_polyline_mode(pc);
3840     }
3843 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3845     /* Freehand mode toggle buttons */
3846     {
3847         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3848         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3849         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3851         {
3852             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3854             GtkTreeIter iter;
3855             gtk_list_store_append( model, &iter );
3856             gtk_list_store_set( model, &iter,
3857                                 0, _("Bezier"),
3858                                 1, _("Create regular Bezier path"),
3859                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3860                                 -1 );
3862             gtk_list_store_append( model, &iter );
3863             gtk_list_store_set( model, &iter,
3864                                 0, _("Spiro"),
3865                                 1, _("Create Spiro path"),
3866                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3867                                 -1 );
3869             if (!tool_is_pencil) {
3870                 gtk_list_store_append( model, &iter );
3871                 gtk_list_store_set( model, &iter,
3872                                     0, _("Zigzag"),
3873                                     1, _("Create a sequence of straight line segments"),
3874                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3875                                     -1 );
3877                 gtk_list_store_append( model, &iter );
3878                 gtk_list_store_set( model, &iter,
3879                                     0, _("Paraxial"),
3880                                     1, _("Create a sequence of paraxial line segments"),
3881                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3882                                     -1 );
3883             }
3885             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3886                                                                 "FreehandModeActionPencil" :
3887                                                                 "FreehandModeActionPen",
3888                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3889             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3891             ege_select_one_action_set_appearance( act, "full" );
3892             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3893             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3894             ege_select_one_action_set_icon_column( act, 2 );
3895             ege_select_one_action_set_icon_size( act, secondarySize );
3896             ege_select_one_action_set_tooltip_column( act, 1  );
3898             ege_select_one_action_set_active( act, freehandMode);
3899             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3900         }
3901     }
3904 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3905     gint shape = ege_select_one_action_get_active( act );
3906     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3907     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3910 /**
3911  * \brief Generate the list of freehand advanced shape option entries.
3912  */
3913 GList * freehand_shape_dropdown_items_list() {
3914     GList *glist = NULL;
3916     glist = g_list_append (glist, _("None"));
3917     glist = g_list_append (glist, _("Triangle in"));
3918     glist = g_list_append (glist, _("Triangle out"));
3919     glist = g_list_append (glist, _("Ellipse"));
3920     glist = g_list_append (glist, _("From clipboard"));
3922     return glist;
3925 static void
3926 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3927     /*advanced shape options */
3928     {
3929         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3930         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3932         GList* items = 0;
3933         gint count = 0;
3934         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3935         {
3936             GtkTreeIter iter;
3937             gtk_list_store_append( model, &iter );
3938             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3939             count++;
3940         }
3941         g_list_free( items );
3942         items = 0;
3943         EgeSelectOneAction* act1 = ege_select_one_action_new(
3944             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3945             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3946         g_object_set( act1, "short_label", _("Shape:"), NULL );
3947         ege_select_one_action_set_appearance( act1, "compact" );
3948         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3949         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3950         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3951         g_object_set_data( holder, "shape_action", act1 );
3952     }
3955 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3957     sp_add_freehand_mode_toggle(mainActions, holder, false);
3958     freehand_add_advanced_shape_options(mainActions, holder, false);
3962 static void
3963 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3965     GtkWidget *tbl = GTK_WIDGET(obj);
3967     GtkAdjustment *adj;
3969     // fixme: make settable
3970     gdouble tolerance = 4;
3972     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3973     gtk_adjustment_set_value(adj, tolerance);
3974     gtk_adjustment_value_changed(adj);
3976     spinbutton_defocus(GTK_OBJECT(tbl));
3979 static void
3980 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3982     // quit if run by the attr_changed listener
3983     if (g_object_get_data( tbl, "freeze" )) {
3984         return;
3985     }
3986     // in turn, prevent listener from responding
3987     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3988     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3989     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3990     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3993 /*
3994 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3995 public:
3996     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3997     {
3998         g_object_set_data(_obj, "prefobserver", this);
3999     }
4000     virtual ~PencilToleranceObserver() {
4001         if (g_object_get_data(_obj, "prefobserver") == this) {
4002             g_object_set_data(_obj, "prefobserver", NULL);
4003         }
4004     }
4005     virtual void notify(Inkscape::Preferences::Entry const &val) {
4006         GObject* tbl = _obj;
4007         if (g_object_get_data( tbl, "freeze" )) {
4008             return;
4009         }
4010         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4012         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4014         double v = val.getDouble(adj->value);
4015         gtk_adjustment_set_value(adj, v);
4016         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4017     }
4018 private:
4019     GObject *_obj;
4020 };
4021 */
4023 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4025     sp_add_freehand_mode_toggle(mainActions, holder, true);
4027     EgeAdjustmentAction* eact = 0;
4029     /* Tolerance */
4030     {
4031         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4032         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4033         eact = create_adjustment_action( "PencilToleranceAction",
4034                                          _("Smoothing:"), _("Smoothing: "),
4035                  _("How much smoothing (simplifying) is applied to the line"),
4036                                          "/tools/freehand/pencil/tolerance",
4037                                          3.0,
4038                                          GTK_WIDGET(desktop->canvas), NULL,
4039                                          holder, TRUE, "altx-pencil",
4040                                          1, 100.0, 0.5, 1.0,
4041                                          labels, values, G_N_ELEMENTS(labels),
4042                                          sp_pencil_tb_tolerance_value_changed,
4043                                          1, 2);
4044         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4045         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4046     }
4048     /* advanced shape options */
4049     freehand_add_advanced_shape_options(mainActions, holder, true);
4051     /* Reset */
4052     {
4053         InkAction* inky = ink_action_new( "PencilResetAction",
4054                                           _("Defaults"),
4055                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4056                                           GTK_STOCK_CLEAR,
4057                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4058         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4059         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4060     }
4062     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4067 //########################
4068 //##       Tweak        ##
4069 //########################
4071 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4073     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4077 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4079     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4080     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4083 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4085     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4086     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4089 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4091     int mode = ege_select_one_action_get_active( act );
4092     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4093     prefs->setInt("/tools/tweak/mode", mode);
4095     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4096     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4097     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4098     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4099     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4100     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4101     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4102         if (doh) gtk_action_set_sensitive (doh, TRUE);
4103         if (dos) gtk_action_set_sensitive (dos, TRUE);
4104         if (dol) gtk_action_set_sensitive (dol, TRUE);
4105         if (doo) gtk_action_set_sensitive (doo, TRUE);
4106         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4107         if (fid) gtk_action_set_sensitive (fid, FALSE);
4108     } else {
4109         if (doh) gtk_action_set_sensitive (doh, FALSE);
4110         if (dos) gtk_action_set_sensitive (dos, FALSE);
4111         if (dol) gtk_action_set_sensitive (dol, FALSE);
4112         if (doo) gtk_action_set_sensitive (doo, FALSE);
4113         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4114         if (fid) gtk_action_set_sensitive (fid, TRUE);
4115     }
4118 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4120     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4121     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4124 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4125     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4126     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4128 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4129     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4130     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4132 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4133     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4134     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4136 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4138     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4141 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4143     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4144     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4146     {
4147         /* Width */
4148         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4149         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4150         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4151                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4152                                                               "/tools/tweak/width", 15,
4153                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4154                                                               1, 100, 1.0, 10.0,
4155                                                               labels, values, G_N_ELEMENTS(labels),
4156                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4157         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4158         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4159         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4160     }
4163     {
4164         /* Force */
4165         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4166         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4167         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4168                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4169                                                               "/tools/tweak/force", 20,
4170                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4171                                                               1, 100, 1.0, 10.0,
4172                                                               labels, values, G_N_ELEMENTS(labels),
4173                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4174         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4175         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4176         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4177     }
4179     /* Mode */
4180     {
4181         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4183         GtkTreeIter iter;
4184         gtk_list_store_append( model, &iter );
4185         gtk_list_store_set( model, &iter,
4186                             0, _("Move mode"),
4187                             1, _("Move objects in any direction"),
4188                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4189                             -1 );
4191         gtk_list_store_append( model, &iter );
4192         gtk_list_store_set( model, &iter,
4193                             0, _("Move in/out mode"),
4194                             1, _("Move objects towards cursor; with Shift from cursor"),
4195                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4196                             -1 );
4198         gtk_list_store_append( model, &iter );
4199         gtk_list_store_set( model, &iter,
4200                             0, _("Move jitter mode"),
4201                             1, _("Move objects in random directions"),
4202                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4203                             -1 );
4205         gtk_list_store_append( model, &iter );
4206         gtk_list_store_set( model, &iter,
4207                             0, _("Scale mode"),
4208                             1, _("Shrink objects, with Shift enlarge"),
4209                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4210                             -1 );
4212         gtk_list_store_append( model, &iter );
4213         gtk_list_store_set( model, &iter,
4214                             0, _("Rotate mode"),
4215                             1, _("Rotate objects, with Shift counterclockwise"),
4216                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4217                             -1 );
4219         gtk_list_store_append( model, &iter );
4220         gtk_list_store_set( model, &iter,
4221                             0, _("Duplicate/delete mode"),
4222                             1, _("Duplicate objects, with Shift delete"),
4223                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4224                             -1 );
4226         gtk_list_store_append( model, &iter );
4227         gtk_list_store_set( model, &iter,
4228                             0, _("Push mode"),
4229                             1, _("Push parts of paths in any direction"),
4230                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4231                             -1 );
4233         gtk_list_store_append( model, &iter );
4234         gtk_list_store_set( model, &iter,
4235                             0, _("Shrink/grow mode"),
4236                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4237                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4238                             -1 );
4240         gtk_list_store_append( model, &iter );
4241         gtk_list_store_set( model, &iter,
4242                             0, _("Attract/repel mode"),
4243                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4244                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4245                             -1 );
4247         gtk_list_store_append( model, &iter );
4248         gtk_list_store_set( model, &iter,
4249                             0, _("Roughen mode"),
4250                             1, _("Roughen parts of paths"),
4251                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4252                             -1 );
4254         gtk_list_store_append( model, &iter );
4255         gtk_list_store_set( model, &iter,
4256                             0, _("Color paint mode"),
4257                             1, _("Paint the tool's color upon selected objects"),
4258                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4259                             -1 );
4261         gtk_list_store_append( model, &iter );
4262         gtk_list_store_set( model, &iter,
4263                             0, _("Color jitter mode"),
4264                             1, _("Jitter the colors of selected objects"),
4265                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4266                             -1 );
4268         gtk_list_store_append( model, &iter );
4269         gtk_list_store_set( model, &iter,
4270                             0, _("Blur mode"),
4271                             1, _("Blur selected objects more; with Shift, blur less"),
4272                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4273                             -1 );
4276         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4277         g_object_set( act, "short_label", _("Mode:"), NULL );
4278         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4279         g_object_set_data( holder, "mode_action", act );
4281         ege_select_one_action_set_appearance( act, "full" );
4282         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4283         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4284         ege_select_one_action_set_icon_column( act, 2 );
4285         ege_select_one_action_set_icon_size( act, secondarySize );
4286         ege_select_one_action_set_tooltip_column( act, 1  );
4288         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4289         ege_select_one_action_set_active( act, mode );
4290         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4292         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4293     }
4295     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4297     {
4298         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4299         ege_output_action_set_use_markup( act, TRUE );
4300         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4302             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4303         g_object_set_data( holder, "tweak_channels_label", act);
4304     }
4306     {
4307         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4308                                                       _("Hue"),
4309                                                       _("In color mode, act on objects' hue"),
4310                                                       NULL,
4311                                                       Inkscape::ICON_SIZE_DECORATION );
4312         //TRANSLATORS:  "H" here stands for hue
4313         g_object_set( act, "short_label", _("H"), NULL );
4314         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4315         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4316         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4317         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4318             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4319         g_object_set_data( holder, "tweak_doh", act);
4320     }
4321     {
4322         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4323                                                       _("Saturation"),
4324                                                       _("In color mode, act on objects' saturation"),
4325                                                       NULL,
4326                                                       Inkscape::ICON_SIZE_DECORATION );
4327         //TRANSLATORS: "S" here stands for Saturation
4328         g_object_set( act, "short_label", _("S"), NULL );
4329         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4330         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4331         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4332         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4333             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4334         g_object_set_data( holder, "tweak_dos", act );
4335     }
4336     {
4337         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4338                                                       _("Lightness"),
4339                                                       _("In color mode, act on objects' lightness"),
4340                                                       NULL,
4341                                                       Inkscape::ICON_SIZE_DECORATION );
4342         //TRANSLATORS: "L" here stands for Lightness
4343         g_object_set( act, "short_label", _("L"), NULL );
4344         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4345         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4346         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4347         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4348             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4349         g_object_set_data( holder, "tweak_dol", act );
4350     }
4351     {
4352         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4353                                                       _("Opacity"),
4354                                                       _("In color mode, act on objects' opacity"),
4355                                                       NULL,
4356                                                       Inkscape::ICON_SIZE_DECORATION );
4357         //TRANSLATORS: "O" here stands for Opacity
4358         g_object_set( act, "short_label", _("O"), NULL );
4359         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4360         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4361         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4362         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4363             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4364         g_object_set_data( holder, "tweak_doo", act );
4365     }
4367     {   /* Fidelity */
4368         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4369         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4370         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4371                                                               _("Fidelity"), _("Fidelity:"),
4372                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4373                                                               "/tools/tweak/fidelity", 50,
4374                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4375                                                               1, 100, 1.0, 10.0,
4376                                                               labels, values, G_N_ELEMENTS(labels),
4377                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4378         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4379         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4380         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4381             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4382         g_object_set_data( holder, "tweak_fidelity", eact );
4383     }
4386     /* Use Pressure button */
4387     {
4388         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4389                                                       _("Pressure"),
4390                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4391                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4392                                                       Inkscape::ICON_SIZE_DECORATION );
4393         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4394         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4395         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4396     }
4401 //########################
4402 //##       Spray        ##
4403 //########################
4405 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4407     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4408     prefs->setDouble( "/tools/spray/width", adj->value );
4411 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4413     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4414     prefs->setDouble( "/tools/spray/mean", adj->value );
4417 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4419     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4420     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4423 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4425     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4426     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4429 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4431     int mode = ege_select_one_action_get_active( act );
4432     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4433     prefs->setInt("/tools/spray/mode", mode);
4436 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4438     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439     prefs->setDouble( "/tools/spray/population", adj->value );
4442 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4444     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4445     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4448 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4450     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4451     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4455 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4457     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4458     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460     {
4461         /* Width */
4462         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4463         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4464         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4465                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4466                                                               "/tools/spray/width", 15,
4467                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4468                                                               1, 100, 1.0, 10.0,
4469                                                               labels, values, G_N_ELEMENTS(labels),
4470                                                               sp_spray_width_value_changed,  1, 0 );
4471         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4472         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4473         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4474     }
4476     {
4477         /* Mean */
4478         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4479         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4480         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4481                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4482                                                               "/tools/spray/mean", 0,
4483                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4484                                                               0, 100, 1.0, 10.0,
4485                                                               labels, values, G_N_ELEMENTS(labels),
4486                                                               sp_spray_mean_value_changed,  1, 0 );
4487         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4488         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4489         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4490     }
4492     {
4493         /* Standard_deviation */
4494         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4495         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4496         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4497                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4498                                                               "/tools/spray/standard_deviation", 70,
4499                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4500                                                               1, 100, 1.0, 10.0,
4501                                                               labels, values, G_N_ELEMENTS(labels),
4502                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4503         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4504         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4505         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4506     }
4508     /* Mode */
4509     {
4510         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4512         GtkTreeIter iter;
4513         gtk_list_store_append( model, &iter );
4514         gtk_list_store_set( model, &iter,
4515                             0, _("Spray with copies"),
4516                             1, _("Spray copies of the initial selection"),
4517                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4518                             -1 );
4520         gtk_list_store_append( model, &iter );
4521         gtk_list_store_set( model, &iter,
4522                             0, _("Spray with clones"),
4523                             1, _("Spray clones of the initial selection"),
4524                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4525                             -1 );
4527         gtk_list_store_append( model, &iter );
4528         gtk_list_store_set( model, &iter,
4529                             0, _("Spray single path"),
4530                             1, _("Spray objects in a single path"),
4531                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4532                             -1 );
4534         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4535         g_object_set( act, "short_label", _("Mode:"), NULL );
4536         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4537         g_object_set_data( holder, "mode_action", act );
4539         ege_select_one_action_set_appearance( act, "full" );
4540         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4541         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4542         ege_select_one_action_set_icon_column( act, 2 );
4543         ege_select_one_action_set_icon_size( act, secondarySize );
4544         ege_select_one_action_set_tooltip_column( act, 1  );
4546         gint mode = prefs->getInt("/tools/spray/mode", 1);
4547         ege_select_one_action_set_active( act, mode );
4548         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4550         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4551     }
4553     {   /* Population */
4554         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4555         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4556         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4557                                                               _("Amount"), _("Amount:"),
4558                                                               _("Adjusts the number of items sprayed per clic."),
4559                                                               "/tools/spray/population", 70,
4560                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4561                                                               1, 100, 1.0, 10.0,
4562                                                               labels, values, G_N_ELEMENTS(labels),
4563                                                               sp_spray_population_value_changed,  1, 0 );
4564         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4565         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4566         g_object_set_data( holder, "spray_population", eact );
4567     }
4569     /* Use Pressure button */
4570     {
4571         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4572                                                       _("Pressure"),
4573                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4574                                                       "use_pressure",
4575                                                       Inkscape::ICON_SIZE_DECORATION );
4576         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4577         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4578         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4579     }
4581     {   /* Rotation */
4582         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4583         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4584         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4585                                                               _("Rotation"), _("Rotation:"),
4586                                                               // xgettext:no-c-format
4587                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4588                                                               "/tools/spray/rotation_variation", 0,
4589                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4590                                                               0, 100, 1.0, 10.0,
4591                                                               labels, values, G_N_ELEMENTS(labels),
4592                                                               sp_spray_rotation_value_changed,  1, 0 );
4593         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4594         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4595         g_object_set_data( holder, "spray_rotation", eact );
4596     }
4598     {   /* Scale */
4599         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4600         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4601         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4602                                                               _("Scale"), _("Scale:"),
4603                                                               // xgettext:no-c-format
4604                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4605                                                               "/tools/spray/scale_variation", 0,
4606                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4607                                                               0, 100, 1.0, 10.0,
4608                                                               labels, values, G_N_ELEMENTS(labels),
4609                                                               sp_spray_scale_value_changed,  1, 0 );
4610         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4611         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4612         g_object_set_data( holder, "spray_scale", eact );
4613     }
4620 //########################
4621 //##     Calligraphy    ##
4622 //########################
4623 static void update_presets_list (GObject *tbl)
4625     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4626     if (g_object_get_data(tbl, "presets_blocked"))
4627         return;
4629     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4630     if (!sel) {
4631         // WTF!? This will cause a segfault if ever reached
4632         //ege_select_one_action_set_active(sel, 0);
4633         return;
4634     }
4636     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4638     int ege_index = 1;
4639     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4640         bool match = true;
4642         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4643         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4644             Glib::ustring entry_name = j->getEntryName();
4645             if (entry_name == "id" || entry_name == "name") continue;
4647             void *widget = g_object_get_data(tbl, entry_name.data());
4648             if (widget) {
4649                 if (GTK_IS_ADJUSTMENT(widget)) {
4650                     double v = j->getDouble();
4651                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4652                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4653                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4654                         match = false;
4655                         break;
4656                     }
4657                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4658                     bool v = j->getBool();
4659                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4660                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4661                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4662                         match = false;
4663                         break;
4664                     }
4665                 }
4666             }
4667         }
4669         if (match) {
4670             // newly added item is at the same index as the
4671             // save command, so we need to change twice for it to take effect
4672             ege_select_one_action_set_active(sel, 0);
4673             ege_select_one_action_set_active(sel, ege_index); // one-based index
4674             return;
4675         }
4676     }
4678     // no match found
4679     ege_select_one_action_set_active(sel, 0);
4682 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4684     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4685     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4686     update_presets_list(tbl);
4689 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4691     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4692     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4693     update_presets_list(tbl);
4696 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4698     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4699     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4700     update_presets_list(tbl);
4703 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4705     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4706     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4707     update_presets_list(tbl);
4710 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4712     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4713     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4714     update_presets_list(tbl);
4717 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4719     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4720     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4721     update_presets_list(tbl);
4724 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4726     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4727     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4728     update_presets_list(tbl);
4731 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4733     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4734     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4735     update_presets_list(tbl);
4738 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4740     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4741     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4742     update_presets_list(tbl);
4745 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4747     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4748     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4749     update_presets_list(tbl);
4752 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4754     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4755     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4756     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4757     update_presets_list(tbl);
4758     if (calligraphy_angle )
4759         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4763 static gchar const *const widget_names[] = {
4764     "width",
4765     "mass",
4766     "wiggle",
4767     "angle",
4768     "thinning",
4769     "tremor",
4770     "flatness",
4771     "cap_rounding",
4772     "usepressure",
4773     "tracebackground",
4774     "usetilt"
4775 };
4778 static void sp_dcc_build_presets_list(GObject *tbl)
4780     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4782     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4783     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4784     gtk_list_store_clear (model);
4786     {
4787         GtkTreeIter iter;
4788         gtk_list_store_append( model, &iter );
4789         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4790     }
4792     // iterate over all presets to populate the list
4793     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4794     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4795     int ii=1;
4797     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4798         GtkTreeIter iter;
4799         Glib::ustring preset_name = prefs->getString(*i + "/name");
4800         gtk_list_store_append( model, &iter );
4801         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4802     }
4804     {
4805         GtkTreeIter iter;
4806         gtk_list_store_append( model, &iter );
4807         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4808         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4809     }
4811     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4813     update_presets_list (tbl);
4816 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4818     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4819     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4820     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4821     if (! desktop) return;
4823     if (g_object_get_data(tbl, "presets_blocked"))
4824         return;
4826     CalligraphicProfileRename::show(desktop);
4827     if ( !CalligraphicProfileRename::applied()) {
4828         // dialog cancelled
4829         update_presets_list (tbl);
4830         return;
4831     }
4832     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4834     if (profile_name.empty()) {
4835         // empty name entered
4836         update_presets_list (tbl);
4837         return;
4838     }
4840     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4842     // If there's a preset with the given name, find it and set save_path appropriately
4843     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4844     int total_presets = presets.size();
4845     int new_index = -1;
4846     Glib::ustring save_path; // profile pref path without a trailing slash
4848     int temp_index = 0;
4849     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4850         Glib::ustring name = prefs->getString(*i + "/name");
4851         if (!name.empty() && profile_name == name) {
4852             new_index = temp_index;
4853             save_path = *i;
4854             break;
4855         }
4856     }
4858     if (new_index == -1) {
4859         // no preset with this name, create
4860         new_index = total_presets + 1;
4861         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4862         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4863         g_free(profile_id);
4864     }
4866     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4867         gchar const *const widget_name = widget_names[i];
4868         void *widget = g_object_get_data(tbl, widget_name);
4869         if (widget) {
4870             if (GTK_IS_ADJUSTMENT(widget)) {
4871                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4872                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4873                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4874             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4875                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4876                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4877                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4878             } else {
4879                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4880             }
4881         } else {
4882             g_warning("Bad key when writing preset: %s\n", widget_name);
4883         }
4884     }
4885     prefs->setString(save_path + "/name", profile_name);
4887     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4888     sp_dcc_build_presets_list (tbl);
4892 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4894     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4896     gint preset_index = ege_select_one_action_get_active( act );
4897     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4898     // even when the preset is not changed. It would be good to replace it with something more
4899     // modern. Index 0 means "No preset", so we don't do anything.
4900     if (preset_index == 0) return;
4902     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4904     if (preset_index == save_presets_index) {
4905         // this is the Save command
4906         sp_dcc_save_profile(NULL, tbl);
4907         return;
4908     }
4910     if (g_object_get_data(tbl, "presets_blocked"))
4911         return;
4913     // preset_index is one-based so we subtract 1
4914     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4915     Glib::ustring preset_path = presets.at(preset_index - 1);
4917     if (!preset_path.empty()) {
4918         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
4920         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4922         // Shouldn't this be std::map?
4923         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4924             Glib::ustring entry_name = i->getEntryName();
4925             if (entry_name == "id" || entry_name == "name") continue;
4926             void *widget = g_object_get_data(tbl, entry_name.data());
4927             if (widget) {
4928                 if (GTK_IS_ADJUSTMENT(widget)) {
4929                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4930                     gtk_adjustment_set_value(adj, i->getDouble());
4931                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4932                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4933                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4934                     gtk_toggle_action_set_active(toggle, i->getBool());
4935                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4936                 } else {
4937                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4938                 }
4939             } else {
4940                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4941             }
4942         }
4943         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4944     }
4948 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4950     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4951     {
4952         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4954         EgeAdjustmentAction* calligraphy_angle = 0;
4956         {
4957         /* Width */
4958         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4959         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4960         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4961                                                               _("Pen Width"), _("Width:"),
4962                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4963                                                               "/tools/calligraphic/width", 15,
4964                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4965                                                               1, 100, 1.0, 10.0,
4966                                                               labels, values, G_N_ELEMENTS(labels),
4967                                                               sp_ddc_width_value_changed,  1, 0 );
4968         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4969         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4970         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4971         }
4973         {
4974         /* Thinning */
4975             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4976             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4977         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4978                                                               _("Stroke Thinning"), _("Thinning:"),
4979                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4980                                                               "/tools/calligraphic/thinning", 10,
4981                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4982                                                               -100, 100, 1, 10.0,
4983                                                               labels, values, G_N_ELEMENTS(labels),
4984                                                               sp_ddc_velthin_value_changed, 1, 0);
4985         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4986         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4987         }
4989         {
4990         /* Angle */
4991         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4992         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4993         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4994                                                               _("Pen Angle"), _("Angle:"),
4995                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4996                                                               "/tools/calligraphic/angle", 30,
4997                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4998                                                               -90.0, 90.0, 1.0, 10.0,
4999                                                               labels, values, G_N_ELEMENTS(labels),
5000                                                               sp_ddc_angle_value_changed, 1, 0 );
5001         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5002         g_object_set_data( holder, "angle_action", eact );
5003         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5004         calligraphy_angle = eact;
5005         }
5007         {
5008         /* Fixation */
5009             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5010         gdouble values[] = {0, 20, 40, 60, 90, 100};
5011         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5012                                                               _("Fixation"), _("Fixation:"),
5013                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5014                                                               "/tools/calligraphic/flatness", 90,
5015                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5016                                                               0.0, 100, 1.0, 10.0,
5017                                                               labels, values, G_N_ELEMENTS(labels),
5018                                                               sp_ddc_flatness_value_changed, 1, 0);
5019         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5020         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5021         }
5023         {
5024         /* Cap Rounding */
5025             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5026         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5027         // TRANSLATORS: "cap" means "end" (both start and finish) here
5028         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5029                                                               _("Cap rounding"), _("Caps:"),
5030                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5031                                                               "/tools/calligraphic/cap_rounding", 0.0,
5032                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5033                                                               0.0, 5.0, 0.01, 0.1,
5034                                                               labels, values, G_N_ELEMENTS(labels),
5035                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5036         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5037         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5038         }
5040         {
5041         /* Tremor */
5042             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5043         gdouble values[] = {0, 10, 20, 40, 60, 100};
5044         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5045                                                               _("Stroke Tremor"), _("Tremor:"),
5046                                                               _("Increase to make strokes rugged and trembling"),
5047                                                               "/tools/calligraphic/tremor", 0.0,
5048                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5049                                                               0.0, 100, 1, 10.0,
5050                                                               labels, values, G_N_ELEMENTS(labels),
5051                                                               sp_ddc_tremor_value_changed, 1, 0);
5053         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5054         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5055         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5056         }
5058         {
5059         /* Wiggle */
5060         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5061         gdouble values[] = {0, 20, 40, 60, 100};
5062         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5063                                                               _("Pen Wiggle"), _("Wiggle:"),
5064                                                               _("Increase to make the pen waver and wiggle"),
5065                                                               "/tools/calligraphic/wiggle", 0.0,
5066                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5067                                                               0.0, 100, 1, 10.0,
5068                                                               labels, values, G_N_ELEMENTS(labels),
5069                                                               sp_ddc_wiggle_value_changed, 1, 0);
5070         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5071         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5072         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5073         }
5075         {
5076         /* Mass */
5077             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5078         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5079         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5080                                                               _("Pen Mass"), _("Mass:"),
5081                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5082                                                               "/tools/calligraphic/mass", 2.0,
5083                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5084                                                               0.0, 100, 1, 10.0,
5085                                                               labels, values, G_N_ELEMENTS(labels),
5086                                                               sp_ddc_mass_value_changed, 1, 0);
5087         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5088         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5089         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5090         }
5093         /* Trace Background button */
5094         {
5095             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5096                                                           _("Trace Background"),
5097                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5098                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5099                                                           Inkscape::ICON_SIZE_DECORATION );
5100             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5101             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5102             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5103             g_object_set_data( holder, "tracebackground", act );
5104         }
5106         /* Use Pressure button */
5107         {
5108             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5109                                                           _("Pressure"),
5110                                                           _("Use the pressure of the input device to alter the width of the pen"),
5111                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5112                                                           Inkscape::ICON_SIZE_DECORATION );
5113             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5114             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5115             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5116             g_object_set_data( holder, "usepressure", act );
5117         }
5119         /* Use Tilt button */
5120         {
5121             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5122                                                           _("Tilt"),
5123                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5124                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5125                                                           Inkscape::ICON_SIZE_DECORATION );
5126             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5127             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5128             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5129             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5130             g_object_set_data( holder, "usetilt", act );
5131         }
5133         /*calligraphic profile */
5134         {
5135             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5136             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5137             ege_select_one_action_set_appearance (act1, "compact");
5138             g_object_set_data (holder, "profile_selector", act1 );
5140             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5142             sp_dcc_build_presets_list (holder);
5144             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5145             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5146         }
5147     }
5151 //########################
5152 //##    Circle / Arc    ##
5153 //########################
5155 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5157     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5158     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5160     if (v1 == 0 && v2 == 0) {
5161         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5162             gtk_action_set_sensitive( ocb, FALSE );
5163             gtk_action_set_sensitive( make_whole, FALSE );
5164         }
5165     } else {
5166         gtk_action_set_sensitive( ocb, TRUE );
5167         gtk_action_set_sensitive( make_whole, TRUE );
5168     }
5171 static void
5172 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5174     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5176     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5177         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5178         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5179     }
5181     // quit if run by the attr_changed listener
5182     if (g_object_get_data( tbl, "freeze" )) {
5183         return;
5184     }
5186     // in turn, prevent listener from responding
5187     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5189     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5191     bool modmade = false;
5192     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5193          items != NULL;
5194          items = items->next)
5195     {
5196         SPItem *item = SP_ITEM(items->data);
5198         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5200             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5201             SPArc *arc = SP_ARC(item);
5203             if (!strcmp(value_name, "start"))
5204                 ge->start = (adj->value * M_PI)/ 180;
5205             else
5206                 ge->end = (adj->value * M_PI)/ 180;
5208             sp_genericellipse_normalize(ge);
5209             ((SPObject *)arc)->updateRepr();
5210             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5212             modmade = true;
5213         }
5214     }
5216     g_free(namespaced_name);
5218     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5220     sp_arctb_sensitivize( tbl, adj->value, other->value );
5222     if (modmade) {
5223         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5224                                    _("Arc: Change start/end"));
5225     }
5227     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5231 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5233     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5236 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5238     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5242 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5244     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5245     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5246         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5247         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5248     }
5250     // quit if run by the attr_changed listener
5251     if (g_object_get_data( tbl, "freeze" )) {
5252         return;
5253     }
5255     // in turn, prevent listener from responding
5256     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5258     bool modmade = false;
5260     if ( ege_select_one_action_get_active(act) != 0 ) {
5261         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5262              items != NULL;
5263              items = items->next)
5264         {
5265             if (SP_IS_ARC((SPItem *) items->data)) {
5266                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5267                 repr->setAttribute("sodipodi:open", "true");
5268                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5269                 modmade = true;
5270             }
5271         }
5272     } else {
5273         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5274              items != NULL;
5275              items = items->next)
5276         {
5277             if (SP_IS_ARC((SPItem *) items->data))    {
5278                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5279                 repr->setAttribute("sodipodi:open", NULL);
5280                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5281                 modmade = true;
5282             }
5283         }
5284     }
5286     if (modmade) {
5287         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5288                                    _("Arc: Change open/closed"));
5289     }
5291     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5294 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5296     GtkAdjustment *adj;
5297     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5298     gtk_adjustment_set_value(adj, 0.0);
5299     gtk_adjustment_value_changed(adj);
5301     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5302     gtk_adjustment_set_value(adj, 0.0);
5303     gtk_adjustment_value_changed(adj);
5305     spinbutton_defocus( GTK_OBJECT(obj) );
5308 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5309                                       gchar const */*old_value*/, gchar const */*new_value*/,
5310                                       bool /*is_interactive*/, gpointer data)
5312     GObject *tbl = G_OBJECT(data);
5314     // quit if run by the _changed callbacks
5315     if (g_object_get_data( tbl, "freeze" )) {
5316         return;
5317     }
5319     // in turn, prevent callbacks from responding
5320     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5322     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5323     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5325     GtkAdjustment *adj1,*adj2;
5326     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5327     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5328     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5329     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5331     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5333     char const *openstr = NULL;
5334     openstr = repr->attribute("sodipodi:open");
5335     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5337     if (openstr) {
5338         ege_select_one_action_set_active( ocb, 1 );
5339     } else {
5340         ege_select_one_action_set_active( ocb, 0 );
5341     }
5343     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5346 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5347     NULL, /* child_added */
5348     NULL, /* child_removed */
5349     arc_tb_event_attr_changed,
5350     NULL, /* content_changed */
5351     NULL  /* order_changed */
5352 };
5355 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5357     int n_selected = 0;
5358     Inkscape::XML::Node *repr = NULL;
5360     purge_repr_listener( tbl, tbl );
5362     for (GSList const *items = selection->itemList();
5363          items != NULL;
5364          items = items->next)
5365     {
5366         if (SP_IS_ARC((SPItem *) items->data)) {
5367             n_selected++;
5368             repr = SP_OBJECT_REPR((SPItem *) items->data);
5369         }
5370     }
5372     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5374     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5375     if (n_selected == 0) {
5376         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5377     } else if (n_selected == 1) {
5378         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5379         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5381         if (repr) {
5382             g_object_set_data( tbl, "repr", repr );
5383             Inkscape::GC::anchor(repr);
5384             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5385             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5386         }
5387     } else {
5388         // FIXME: implement averaging of all parameters for multiple selected
5389         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5390         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5391         sp_arctb_sensitivize( tbl, 1, 0 );
5392     }
5396 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5398     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5400     EgeAdjustmentAction* eact = 0;
5401     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5404     {
5405         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5406         ege_output_action_set_use_markup( act, TRUE );
5407         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5408         g_object_set_data( holder, "mode_action", act );
5409     }
5411     /* Start */
5412     {
5413         eact = create_adjustment_action( "ArcStartAction",
5414                                          _("Start"), _("Start:"),
5415                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5416                                          "/tools/shapes/arc/start", 0.0,
5417                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5418                                          -360.0, 360.0, 1.0, 10.0,
5419                                          0, 0, 0,
5420                                          sp_arctb_start_value_changed);
5421         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5422     }
5424     /* End */
5425     {
5426         eact = create_adjustment_action( "ArcEndAction",
5427                                          _("End"), _("End:"),
5428                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5429                                          "/tools/shapes/arc/end", 0.0,
5430                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5431                                          -360.0, 360.0, 1.0, 10.0,
5432                                          0, 0, 0,
5433                                          sp_arctb_end_value_changed);
5434         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5435     }
5437     /* Segments / Pie checkbox */
5438     {
5439         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5441         GtkTreeIter iter;
5442         gtk_list_store_append( model, &iter );
5443         gtk_list_store_set( model, &iter,
5444                             0, _("Closed arc"),
5445                             1, _("Switch to segment (closed shape with two radii)"),
5446                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5447                             -1 );
5449         gtk_list_store_append( model, &iter );
5450         gtk_list_store_set( model, &iter,
5451                             0, _("Open Arc"),
5452                             1, _("Switch to arc (unclosed shape)"),
5453                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5454                             -1 );
5456         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5457         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5458         g_object_set_data( holder, "open_action", act );
5460         ege_select_one_action_set_appearance( act, "full" );
5461         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5462         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5463         ege_select_one_action_set_icon_column( act, 2 );
5464         ege_select_one_action_set_icon_size( act, secondarySize );
5465         ege_select_one_action_set_tooltip_column( act, 1  );
5467         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5468         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5469         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5470     }
5472     /* Make Whole */
5473     {
5474         InkAction* inky = ink_action_new( "ArcResetAction",
5475                                           _("Make whole"),
5476                                           _("Make the shape a whole ellipse, not arc or segment"),
5477                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5478                                           secondarySize );
5479         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5480         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5481         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5482         g_object_set_data( holder, "make_whole", inky );
5483     }
5485     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5486     // sensitivize make whole and open checkbox
5487     {
5488         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5489         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5490         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5491     }
5494     sigc::connection *connection = new sigc::connection(
5495         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5496         );
5497     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5498     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5504 // toggle button callbacks and updaters
5506 //########################
5507 //##      Dropper       ##
5508 //########################
5510 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5511     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5512     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5513     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5514     if ( set_action ) {
5515         if ( gtk_toggle_action_get_active( act ) ) {
5516             gtk_action_set_sensitive( set_action, TRUE );
5517         } else {
5518             gtk_action_set_sensitive( set_action, FALSE );
5519         }
5520     }
5522     spinbutton_defocus(GTK_OBJECT(tbl));
5525 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5526     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5527     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5528     spinbutton_defocus(GTK_OBJECT(tbl));
5532 /**
5533  * Dropper auxiliary toolbar construction and setup.
5534  *
5535  * TODO: Would like to add swatch of current color.
5536  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5537  *       can drag and drop places. Will provide a nice mixing palette.
5538  */
5539 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5541     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5542     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5544     {
5545         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5546         ege_output_action_set_use_markup( act, TRUE );
5547         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5548     }
5550     {
5551         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5552                                                       _("Pick opacity"),
5553                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5554                                                       NULL,
5555                                                       Inkscape::ICON_SIZE_DECORATION );
5556         g_object_set( act, "short_label", _("Pick"), NULL );
5557         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5558         g_object_set_data( holder, "pick_action", act );
5559         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5560         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5561     }
5563     {
5564         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5565                                                       _("Assign opacity"),
5566                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5567                                                       NULL,
5568                                                       Inkscape::ICON_SIZE_DECORATION );
5569         g_object_set( act, "short_label", _("Assign"), NULL );
5570         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5571         g_object_set_data( holder, "set_action", act );
5572         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5573         // make sure it's disabled if we're not picking alpha
5574         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5575         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5576     }
5580 //########################
5581 //##      LPETool       ##
5582 //########################
5584 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5586 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5587 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5589     using namespace Inkscape::LivePathEffect;
5591     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5592     SPEventContext *ec = desktop->event_context;
5593     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5594         return;
5595     }
5597     // only take action if run by the attr_changed listener
5598     if (!g_object_get_data(tbl, "freeze")) {
5599         // in turn, prevent listener from responding
5600         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5602         gint mode = ege_select_one_action_get_active(act);
5603         EffectType type = lpesubtools[mode].type;
5605         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5606         bool success = lpetool_try_construction(lc, type);
5607         if (success) {
5608             // since the construction was already performed, we set the state back to inactive
5609             ege_select_one_action_set_active(act, 0);
5610             mode = 0;
5611         } else {
5612             // switch to the chosen subtool
5613             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5614         }
5616         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5617             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5618             prefs->setInt( "/tools/lpetool/mode", mode );
5619         }
5621         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5622     }
5625 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5627     SPEventContext *ec = selection->desktop()->event_context;
5628     if (!SP_IS_LPETOOL_CONTEXT(ec))
5629         return;
5631     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5634 void
5635 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5637     using namespace Inkscape::LivePathEffect;
5638     SPEventContext *ec = selection->desktop()->event_context;
5639     if (!SP_IS_LPETOOL_CONTEXT(ec))
5640         return;
5641     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5643     lpetool_delete_measuring_items(lc);
5644     lpetool_create_measuring_items(lc, selection);
5646     // activate line segment combo box if a single item with LPELineSegment is selected
5647     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5648     SPItem *item = selection->singleItem();
5649     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5650         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5651         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5652         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5653             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5654             g_object_set_data(tbl, "currentlpe", lpe);
5655             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5656             gtk_action_set_sensitive(w, TRUE);
5657             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5658         } else {
5659             g_object_set_data(tbl, "currentlpe", NULL);
5660             g_object_set_data(tbl, "currentlpeitem", NULL);
5661             gtk_action_set_sensitive(w, FALSE);
5662         }
5663     } else {
5664         g_object_set_data(tbl, "currentlpe", NULL);
5665         g_object_set_data(tbl, "currentlpeitem", NULL);
5666         gtk_action_set_sensitive(w, FALSE);
5667     }
5670 static void
5671 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5672     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5673     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5675     bool show = gtk_toggle_action_get_active( act );
5676     prefs->setBool("/tools/lpetool/show_bbox",  show);
5678     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5679         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5680         lpetool_context_reset_limiting_bbox(lc);
5681     }
5684 static void
5685 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5686     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5687     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5688         return;
5690     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5691     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5692     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5693         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5694         bool show = gtk_toggle_action_get_active( act );
5695         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5696         lpetool_show_measuring_info(lc, show);
5697         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5698     }
5701 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5702     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5703     SPUnit const *unit = tracker->getActiveUnit();
5704     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5705     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5707     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5708     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5709         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5710         lpetool_delete_measuring_items(lc);
5711         lpetool_create_measuring_items(lc);
5712     }
5715 static void
5716 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5717     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5718     Inkscape::Selection *selection = desktop->selection;
5720     Geom::OptRect bbox = selection->bounds();
5722     if (bbox) {
5723         Geom::Point A(bbox->min());
5724         Geom::Point B(bbox->max());
5726         A *= desktop->doc2dt();
5727         B *= desktop->doc2dt();
5729         // TODO: should we provide a way to store points in prefs?
5730         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5731         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5732         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5733         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5734         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5736         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5737     }
5739     gtk_toggle_action_set_active(act, false);
5742 static void
5743 sp_line_segment_build_list(GObject *tbl)
5745     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5747     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5748     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5749     gtk_list_store_clear (model);
5751     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5752     {
5753         GtkTreeIter iter;
5754         gtk_list_store_append( model, &iter );
5755         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5756         gtk_list_store_append( model, &iter );
5757         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5758         gtk_list_store_append( model, &iter );
5759         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5760         gtk_list_store_append( model, &iter );
5761         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5762     }
5764     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5767 static void
5768 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5769     using namespace Inkscape::LivePathEffect;
5771     // quit if run by the attr_changed listener
5772     if (g_object_get_data(tbl, "freeze")) {
5773         return;
5774     }
5776     // in turn, prevent listener from responding
5777     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5779     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5780     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5781     if (lpeitem) {
5782         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5783         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5784         sp_lpe_item_update_patheffect(lpeitem, true, true);
5785     }
5787     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5790 static void
5791 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5792     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5794     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5795         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5796     }
5797     gtk_toggle_action_set_active(act, false);
5800 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5802     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5803     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5804     g_object_set_data(holder, "tracker", tracker);
5805     SPUnit const *unit = tracker->getActiveUnit();
5807     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5808     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5810     /** Automatically create a list of LPEs that get added to the toolbar **/
5811     {
5812         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5814         GtkTreeIter iter;
5816         // the first toggle button represents the state that no subtool is active (remove this when
5817         // this can be modeled by EgeSelectOneAction or some other action)
5818         gtk_list_store_append( model, &iter );
5819         gtk_list_store_set( model, &iter,
5820                             0, _("All inactive"),
5821                             1, _("No geometric tool is active"),
5822                             2, "draw-geometry-inactive",
5823                             -1 );
5825         Inkscape::LivePathEffect::EffectType type;
5826         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5827             type =  lpesubtools[i].type;
5828             gtk_list_store_append( model, &iter );
5829             gtk_list_store_set( model, &iter,
5830                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5831                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5832                                 2, lpesubtools[i].icon_name,
5833                                 -1 );
5834         }
5836         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5837         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5838         g_object_set_data( holder, "lpetool_mode_action", act );
5840         ege_select_one_action_set_appearance( act, "full" );
5841         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5842         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5843         ege_select_one_action_set_icon_column( act, 2 );
5844         ege_select_one_action_set_tooltip_column( act, 1  );
5846         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5847         ege_select_one_action_set_active( act, lpeToolMode );
5848         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5849     }
5851     /* Show limiting bounding box */
5852     {
5853         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5854                                                       _("Show limiting bounding box"),
5855                                                       _("Show bounding box (used to cut infinite lines)"),
5856                                                       "show-bounding-box",
5857                                                       Inkscape::ICON_SIZE_DECORATION );
5858         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5859         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5860         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5861     }
5863     /* Set limiting bounding box to bbox of current selection */
5864     {
5865         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5866                                                       _("Get limiting bounding box from selection"),
5867                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5868                                                       "draw-geometry-set-bounding-box",
5869                                                       Inkscape::ICON_SIZE_DECORATION );
5870         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5871         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5872         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5873     }
5876     /* Combo box to choose line segment type */
5877     {
5878         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5879         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5880         ege_select_one_action_set_appearance (act, "compact");
5881         g_object_set_data (holder, "lpetool_line_segment_action", act );
5883         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5885         sp_line_segment_build_list (holder);
5887         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5888         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5889         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5890     }
5892     /* Display measuring info for selected items */
5893     {
5894         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5895                                                       _("Display measuring info"),
5896                                                       _("Display measuring info for selected items"),
5897                                                       "draw-geometry-show-measuring-info",
5898                                                       Inkscape::ICON_SIZE_DECORATION );
5899         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5900         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5901         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5902     }
5904     // add the units menu
5905     {
5906         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5907         gtk_action_group_add_action( mainActions, act );
5908         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5909         g_object_set_data(holder, "lpetool_units_action", act);
5910         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5911     }
5913     /* Open LPE dialog (to adapt parameters numerically) */
5914     {
5915         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5916                                                       _("Open LPE dialog"),
5917                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5918                                                       "dialog-geometry",
5919                                                       Inkscape::ICON_SIZE_DECORATION );
5920         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5921         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5922         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5923     }
5925     //watch selection
5926     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5928     sigc::connection *c_selection_modified =
5929         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5930                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5931     pool->add_connection ("selection-modified", c_selection_modified);
5933     sigc::connection *c_selection_changed =
5934         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5935                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5936     pool->add_connection ("selection-changed", c_selection_changed);
5939 //########################
5940 //##       Eraser       ##
5941 //########################
5943 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5945     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5946     prefs->setDouble( "/tools/eraser/width", adj->value );
5947     update_presets_list(tbl);
5950 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5952     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5953     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5954     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5955         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5956         prefs->setBool( "/tools/eraser/mode", eraserMode );
5957     }
5959     // only take action if run by the attr_changed listener
5960     if (!g_object_get_data( tbl, "freeze" )) {
5961         // in turn, prevent listener from responding
5962         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5964         if ( eraserMode != 0 ) {
5965         } else {
5966         }
5967         // TODO finish implementation
5969         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5970     }
5973 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5975     {
5976         /* Width */
5977         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5978         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5979         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5980                                                               _("Pen Width"), _("Width:"),
5981                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5982                                                               "/tools/eraser/width", 15,
5983                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5984                                                               1, 100, 1.0, 10.0,
5985                                                               labels, values, G_N_ELEMENTS(labels),
5986                                                               sp_erc_width_value_changed, 1, 0);
5987         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5988         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5989         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5990     }
5992     {
5993         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5995         GtkTreeIter iter;
5996         gtk_list_store_append( model, &iter );
5997         gtk_list_store_set( model, &iter,
5998                             0, _("Delete"),
5999                             1, _("Delete objects touched by the eraser"),
6000                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6001                             -1 );
6003         gtk_list_store_append( model, &iter );
6004         gtk_list_store_set( model, &iter,
6005                             0, _("Cut"),
6006                             1, _("Cut out from objects"),
6007                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6008                             -1 );
6010         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6011         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6012         g_object_set_data( holder, "eraser_mode_action", act );
6014         ege_select_one_action_set_appearance( act, "full" );
6015         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6016         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6017         ege_select_one_action_set_icon_column( act, 2 );
6018         ege_select_one_action_set_tooltip_column( act, 1  );
6020         /// @todo Convert to boolean?
6021         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6022         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6023         ege_select_one_action_set_active( act, eraserMode );
6024         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6025     }
6029 //########################
6030 //##    Text Toolbox    ##
6031 //########################
6032 /*
6033 static void
6034 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6036     //Call back for letter sizing spinbutton
6039 static void
6040 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6042     //Call back for line height spinbutton
6045 static void
6046 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6048     //Call back for horizontal kerning spinbutton
6051 static void
6052 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6054     //Call back for vertical kerning spinbutton
6057 static void
6058 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6060     //Call back for letter rotation spinbutton
6061 }*/
6063 namespace {
6065 void
6066 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6068     // quit if run by the _changed callbacks
6069     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6070         return;
6071     }
6073     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6075     SPStyle *query =
6076         sp_style_new (SP_ACTIVE_DOCUMENT);
6078     int result_family =
6079         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6081     int result_style =
6082         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6084     int result_numbers =
6085         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6087     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6089     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6090     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6091         // there are no texts in selection, read from prefs
6093         sp_style_read_from_prefs(query, "/tools/text");
6095         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6096             // do not reset the toolbar style from prefs if we already did it last time
6097             sp_style_unref(query);
6098             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6099             return;
6100         }
6101         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6102     } else {
6103         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6104     }
6106     if (query->text)
6107     {
6108         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6109             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6110             gtk_entry_set_text (GTK_ENTRY (entry), "");
6112         } else if (query->text->font_specification.value || query->text->font_family.value) {
6114             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6115             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6117             // Get the font that corresponds
6118             Glib::ustring familyName;
6120             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6121             if (font) {
6122                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6123                 font->Unref();
6124                 font = NULL;
6125             }
6127             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6129             Gtk::TreeIter iter;
6130             try {
6131                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6132                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6133                 iter = model->get_iter(path);
6134             } catch (...) {
6135                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6136                 sp_style_unref(query);
6137                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6138                 return;
6139             }
6141             combo->set_active (iter);
6142         }
6144         //Size
6145         {
6146             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6147             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6148             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6149             g_free(str);
6150         }
6152         //Anchor
6153         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6154         {
6155             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6156             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6157             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6158             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6159         }
6160         else
6161         {
6162             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6163             {
6164                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6165                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6166                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6167                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6168             }
6169             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6170             {
6171                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6172                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6173                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6174                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6175             }
6176             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6177             {
6178                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6179                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6180                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6181                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6182             }
6183         }
6185         //Style
6186         {
6187             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6189             gboolean active = gtk_toggle_button_get_active (button);
6190             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));
6192             if (active != check)
6193             {
6194                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6195                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6196                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6197             }
6198         }
6200         {
6201             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6203             gboolean active = gtk_toggle_button_get_active (button);
6204             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6206             if (active != check)
6207             {
6208                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6209                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6210                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6211             }
6212         }
6214         //Orientation
6215         //locking both buttons, changing one affect all group (both)
6216         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6217         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6219         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6220         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6222         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6223         {
6224             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6225         }
6226         else
6227         {
6228             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6229         }
6230         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6231         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6232     }
6234     sp_style_unref(query);
6236     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6239 void
6240 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6242     sp_text_toolbox_selection_changed (selection, tbl);
6245 void
6246 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6248     sp_text_toolbox_selection_changed (NULL, tbl);
6251 void
6252 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6253                                 GObject             *tbl)
6255     // quit if run by the _changed callbacks
6256     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6257         return;
6258     }
6260     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6262     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6263     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6264     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6266     //g_print ("family changed to: %s\n", family);
6268     SPStyle *query =
6269         sp_style_new (SP_ACTIVE_DOCUMENT);
6271     int result_fontspec =
6272         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6274     SPCSSAttr *css = sp_repr_css_attr_new ();
6276     // First try to get the font spec from the stored value
6277     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6279     if (fontSpec.empty()) {
6280         // Construct a new font specification if it does not yet exist
6281         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6282         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6283         fontFromStyle->Unref();
6284     }
6286     if (!fontSpec.empty()) {
6288         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6290         if (!newFontSpec.empty()) {
6292             if (fontSpec != newFontSpec) {
6294                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6296                 if (font) {
6297                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6299                     // Set all the these just in case they were altered when finding the best
6300                     // match for the new family and old style...
6302                     gchar c[256];
6304                     font->Family(c, 256);
6306                     sp_repr_css_set_property (css, "font-family", c);
6308                     font->Attribute( "weight", c, 256);
6309                     sp_repr_css_set_property (css, "font-weight", c);
6311                     font->Attribute("style", c, 256);
6312                     sp_repr_css_set_property (css, "font-style", c);
6314                     font->Attribute("stretch", c, 256);
6315                     sp_repr_css_set_property (css, "font-stretch", c);
6317                     font->Attribute("variant", c, 256);
6318                     sp_repr_css_set_property (css, "font-variant", c);
6320                     font->Unref();
6321                 }
6322             }
6324         } else {
6325             // If the old font on selection (or default) was not existing on the system,
6326             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6327             // setting the family reported by the family chooser.
6329             //g_print ("fallback setting family: %s\n", family);
6330             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6331             sp_repr_css_set_property (css, "font-family", family);
6332         }
6333     }
6335     // If querying returned nothing, set the default style of the tool (for new texts)
6336     if (result_fontspec == QUERY_STYLE_NOTHING)
6337     {
6338         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6339         prefs->mergeStyle("/tools/text/style", css);
6340         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6341     }
6342     else
6343     {
6344         sp_desktop_set_style (desktop, css, true, true);
6345     }
6347     sp_style_unref(query);
6349     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6350                                    _("Text: Change font family"));
6351     sp_repr_css_attr_unref (css);
6353     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6355     // unfreeze
6356     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6358     // focus to canvas
6359     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6363 void
6364 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6365                                    gpointer          data)
6367     if (g_object_get_data (G_OBJECT (button), "block")) return;
6368     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6369     int prop = GPOINTER_TO_INT(data);
6371     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6373     // move the x of all texts to preserve the same bbox
6374     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6375     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6376         if (SP_IS_TEXT((SPItem *) items->data)) {
6377             SPItem *item = SP_ITEM(items->data);
6379             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6380             // below, variable names suggest horizontal move, but we check the writing direction
6381             // and move in the corresponding axis
6382             int axis;
6383             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6384                 axis = NR::X;
6385             } else {
6386                 axis = NR::Y;
6387             }
6389             Geom::OptRect bbox
6390                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6391             if (!bbox)
6392                 continue;
6393             double width = bbox->dimensions()[axis];
6394             // If you want to align within some frame, other than the text's own bbox, calculate
6395             // the left and right (or top and bottom for tb text) slacks of the text inside that
6396             // frame (currently unused)
6397             double left_slack = 0;
6398             double right_slack = 0;
6399             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6400             double move = 0;
6401             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6402                 switch (prop) {
6403                     case 0:
6404                         move = -left_slack;
6405                         break;
6406                     case 1:
6407                         move = width/2 + (right_slack - left_slack)/2;
6408                         break;
6409                     case 2:
6410                         move = width + right_slack;
6411                         break;
6412                 }
6413             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6414                 switch (prop) {
6415                     case 0:
6416                         move = -width/2 - left_slack;
6417                         break;
6418                     case 1:
6419                         move = (right_slack - left_slack)/2;
6420                         break;
6421                     case 2:
6422                         move = width/2 + right_slack;
6423                         break;
6424                 }
6425             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6426                 switch (prop) {
6427                     case 0:
6428                         move = -width - left_slack;
6429                         break;
6430                     case 1:
6431                         move = -width/2 + (right_slack - left_slack)/2;
6432                         break;
6433                     case 2:
6434                         move = right_slack;
6435                         break;
6436                 }
6437             }
6438             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6439             if (axis == NR::X) {
6440                 XY = XY + Geom::Point (move, 0);
6441             } else {
6442                 XY = XY + Geom::Point (0, move);
6443             }
6444             SP_TEXT(item)->attributes.setFirstXY(XY);
6445             SP_OBJECT(item)->updateRepr();
6446             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6447         }
6448     }
6450     SPCSSAttr *css = sp_repr_css_attr_new ();
6451     switch (prop)
6452     {
6453         case 0:
6454         {
6455             sp_repr_css_set_property (css, "text-anchor", "start");
6456             sp_repr_css_set_property (css, "text-align", "start");
6457             break;
6458         }
6459         case 1:
6460         {
6461             sp_repr_css_set_property (css, "text-anchor", "middle");
6462             sp_repr_css_set_property (css, "text-align", "center");
6463             break;
6464         }
6466         case 2:
6467         {
6468             sp_repr_css_set_property (css, "text-anchor", "end");
6469             sp_repr_css_set_property (css, "text-align", "end");
6470             break;
6471         }
6473         case 3:
6474         {
6475             sp_repr_css_set_property (css, "text-anchor", "start");
6476             sp_repr_css_set_property (css, "text-align", "justify");
6477             break;
6478         }
6479     }
6481     SPStyle *query =
6482         sp_style_new (SP_ACTIVE_DOCUMENT);
6483     int result_numbers =
6484         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6486     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6487     if (result_numbers == QUERY_STYLE_NOTHING)
6488     {
6489         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6490         prefs->mergeStyle("/tools/text/style", css);
6491     }
6493     sp_style_unref(query);
6495     sp_desktop_set_style (desktop, css, true, true);
6496     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6497                                    _("Text: Change alignment"));
6498     sp_repr_css_attr_unref (css);
6500     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6503 void
6504 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6505                                gpointer          data)
6507     if (g_object_get_data (G_OBJECT (button), "block")) return;
6509     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6510     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6511     int          prop       = GPOINTER_TO_INT(data);
6512     bool         active     = gtk_toggle_button_get_active (button);
6514     SPStyle *query =
6515         sp_style_new (SP_ACTIVE_DOCUMENT);
6517     int result_fontspec =
6518         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6520     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6521     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6522     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6524     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6525     Glib::ustring newFontSpec = "";
6527     if (fontSpec.empty()) {
6528         // Construct a new font specification if it does not yet exist
6529         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6530         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6531         fontFromStyle->Unref();
6532     }
6534     bool nochange = true;
6535     switch (prop)
6536     {
6537         case 0:
6538         {
6539             if (!fontSpec.empty()) {
6540                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6541                 if (!newFontSpec.empty()) {
6542                     // Don't even set the bold if the font didn't exist on the system
6543                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6544                     nochange = false;
6545                 }
6546             }
6547             // set or reset the button according
6548             if(nochange) {
6549                 gboolean check = gtk_toggle_button_get_active (button);
6551                 if (active != check)
6552                 {
6553                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6554                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6555                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6556                 }
6557             }
6559             break;
6560         }
6562         case 1:
6563         {
6564             if (!fontSpec.empty()) {
6565                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6566                 if (!newFontSpec.empty()) {
6567                     // Don't even set the italic if the font didn't exist on the system
6568                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6569                     nochange = false;
6570                 }
6571             }
6572             if(nochange) {
6573                 gboolean check = gtk_toggle_button_get_active (button);
6575                 if (active != check)
6576                 {
6577                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6578                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6579                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6580                 }
6581             }
6582             break;
6583         }
6584     }
6586     if (!newFontSpec.empty()) {
6587         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6588     }
6590     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6591     if (result_fontspec == QUERY_STYLE_NOTHING)
6592     {
6593         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6594         prefs->mergeStyle("/tools/text/style", css);
6595     }
6597     sp_style_unref(query);
6599     sp_desktop_set_style (desktop, css, true, true);
6600     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6601                                    _("Text: Change font style"));
6602     sp_repr_css_attr_unref (css);
6604     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6607 void
6608 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6609                                      gpointer         data)
6611     if (g_object_get_data (G_OBJECT (button), "block")) {
6612         return;
6613     }
6615     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6616     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6617     int          prop       = GPOINTER_TO_INT(data);
6619     switch (prop)
6620     {
6621         case 0:
6622         {
6623             sp_repr_css_set_property (css, "writing-mode", "lr");
6624             break;
6625         }
6627         case 1:
6628         {
6629             sp_repr_css_set_property (css, "writing-mode", "tb");
6630             break;
6631         }
6632     }
6634     SPStyle *query =
6635         sp_style_new (SP_ACTIVE_DOCUMENT);
6636     int result_numbers =
6637         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6639     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6640     if (result_numbers == QUERY_STYLE_NOTHING)
6641     {
6642         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6643         prefs->mergeStyle("/tools/text/style", css);
6644     }
6646     sp_desktop_set_style (desktop, css, true, true);
6647     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6648                                    _("Text: Change orientation"));
6649     sp_repr_css_attr_unref (css);
6651     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6654 gboolean
6655 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6657     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6658     if (!desktop) return FALSE;
6660     switch (get_group0_keyval (event)) {
6661         case GDK_KP_Enter: // chosen
6662         case GDK_Return:
6663             // unfreeze and update, which will defocus
6664             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6665             sp_text_toolbox_family_changed (NULL, tbl);
6666             return TRUE; // I consumed the event
6667             break;
6668         case GDK_Escape:
6669             // defocus
6670             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6671             return TRUE; // I consumed the event
6672             break;
6673     }
6674     return FALSE;
6677 gboolean
6678 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6680     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6681     if (!desktop) return FALSE;
6683     switch (get_group0_keyval (event)) {
6684         case GDK_KP_Enter:
6685         case GDK_Return:
6686         case GDK_Escape: // defocus
6687             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6688             return TRUE; // I consumed the event
6689             break;
6690         case GDK_w:
6691         case GDK_W:
6692             if (event->state & GDK_CONTROL_MASK) {
6693                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6694                 return TRUE; // I consumed the event
6695             }
6696             break;
6697     }
6698     return FALSE;
6702 void
6703 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6704                                GObject     *tbl)
6706      // quit if run by the _changed callbacks
6707     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6708         return;
6709     }
6711     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6713    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6715     // If this is not from selecting a size in the list (in which case get_active will give the
6716     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6717     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6718     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6719    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6720         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6721         return;
6722    }
6724     gdouble value = -1;
6725     {
6726         gchar *endptr;
6727         gchar *const text = gtk_combo_box_get_active_text(cbox);
6728         if (text) {
6729             value = g_strtod(text, &endptr);
6730             if (endptr == text) {  // Conversion failed, non-numeric input.
6731                 value = -1;
6732             }
6733             g_free(text);
6734         }
6735     }
6736     if (value <= 0) {
6737         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6738         return; // could not parse value
6739     }
6741     SPCSSAttr *css = sp_repr_css_attr_new ();
6742     Inkscape::CSSOStringStream osfs;
6743     osfs << value;
6744     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6746     SPStyle *query =
6747         sp_style_new (SP_ACTIVE_DOCUMENT);
6748     int result_numbers =
6749         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6751     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6752     if (result_numbers == QUERY_STYLE_NOTHING)
6753     {
6754         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6755         prefs->mergeStyle("/tools/text/style", css);
6756     }
6758     sp_style_unref(query);
6760     sp_desktop_set_style (desktop, css, true, true);
6761     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6762                                    _("Text: Change font size"));
6763     sp_repr_css_attr_unref (css);
6765     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6767     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6770 gboolean
6771 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6773     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6774     if (!desktop) return FALSE;
6776     if (!g_object_get_data (tbl, "esc-pressed")) {
6777         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6778         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6779         sp_text_toolbox_size_changed (cbox, tbl);
6780         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6781     }
6782     return FALSE; // I consumed the event
6786 gboolean
6787 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6789     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6790     if (!desktop) return FALSE;
6792     switch (get_group0_keyval (event)) {
6793         case GDK_Escape: // defocus
6794             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6795             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6796             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6797             return TRUE; // I consumed the event
6798             break;
6799         case GDK_Return: // defocus
6800         case GDK_KP_Enter:
6801             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6802             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6803             sp_text_toolbox_size_changed (cbox, tbl);
6804             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6805             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6806             return TRUE; // I consumed the event
6807             break;
6808     }
6809     return FALSE;
6812 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6813 // does not work!
6814 gboolean
6815 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6816                                  GdkEventFocus    */*event*/,
6817                                  GObject          *tbl)
6819     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6820     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6821     return FALSE;
6824 gboolean
6825 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6826                                  GdkEventFocus    */*event*/,
6827                                  GObject          *tbl)
6829     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6830     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6831     return FALSE;
6834 void
6835 cell_data_func  (GtkCellLayout */*cell_layout*/,
6836                  GtkCellRenderer   *cell,
6837                  GtkTreeModel      *tree_model,
6838                  GtkTreeIter       *iter,
6839                  gpointer           /*data*/)
6841     gchar *family;
6842     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6843     gchar *const family_escaped = g_markup_escape_text(family, -1);
6845     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6846     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6847     if (show_sample) {
6849         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6850         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6852     std::stringstream markup;
6853     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6854            << family_escaped << "'>" << sample_escaped << "</span>";
6855     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6857         g_free(sample_escaped);
6858     } else {
6859         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6860     }
6862     g_free(family);
6863     g_free(family_escaped);
6866 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6867                                                 GtkTreeModel       *model,
6868                                                 GtkTreeIter        *iter,
6869                                                 GObject            *tbl)
6871     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6872     // press Enter again after choosing a completion)
6873     gchar *family = 0;
6874     gtk_tree_model_get(model, iter, 0, &family, -1);
6876     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6877     gtk_entry_set_text (GTK_ENTRY (entry), family);
6879     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6880     sp_text_toolbox_family_changed (NULL, tbl);
6881     return TRUE;
6885 static void
6886 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6887     GtkEntry *entry;
6888     GtkEntryCompletion *completion;
6889     GtkTreeModel *model;
6891     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6892     completion = gtk_entry_completion_new();
6893     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6894     gtk_entry_completion_set_model(completion, model);
6895     gtk_entry_completion_set_text_column(completion, 0);
6896     gtk_entry_completion_set_inline_completion(completion, FALSE);
6897     gtk_entry_completion_set_inline_selection(completion, FALSE);
6898     gtk_entry_completion_set_popup_completion(completion, TRUE);
6899     gtk_entry_set_completion(entry, completion);
6901     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6903     g_object_unref(completion);
6906 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6907                                       void */*property*/,
6908                                       GObject *tbl)
6910   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6912   gboolean shown;
6913   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6914   if (shown) {
6915          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6916          //g_print("POP: notify: SHOWN\n");
6917   } else {
6918          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6920          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6921          // find out if the drop down was closed by Enter and if so, manually update (only
6922          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6923          GdkEvent *ev = gtk_get_current_event();
6924          if (ev) {
6925              //g_print ("ev type: %d\n", ev->type);
6926              if (ev->type == GDK_KEY_PRESS) {
6927                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6928                      case GDK_KP_Enter: // chosen
6929                      case GDK_Return:
6930                      {
6931                          // make sure the chosen one is inserted into the entry
6932                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6933                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6934                          GtkTreeIter iter;
6935                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6936                          if (has_active) {
6937                              gchar *family;
6938                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6939                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6940                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6941                          }
6943                          // update
6944                          sp_text_toolbox_family_changed (NULL, tbl);
6945                          break;
6946                      }
6947                  }
6948              }
6949          }
6951          // regardless of whether we updated, defocus the widget
6952          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6953          if (desktop)
6954              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6955          //g_print("POP: notify: HIDDEN\n");
6956   }
6959 GtkWidget*
6960 sp_text_toolbox_new (SPDesktop *desktop)
6962     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6963     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6965     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6966     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6968     GtkTooltips *tt = gtk_tooltips_new();
6970     ////////////Family
6971     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6972     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6974     gtk_rc_parse_string (
6975        "style \"dropdown-as-list-style\"\n"
6976        "{\n"
6977        "    GtkComboBox::appears-as-list = 1\n"
6978        "}\n"
6979        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6980     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6981     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6983     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6985     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6987     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6988     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6990     // expand the field a bit so as to view more of the previews in the drop-down
6991     GtkRequisition req;
6992     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6993     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6995     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6996     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6998     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6999     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7000              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7001     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7002     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7003     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7005     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7006     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7008     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7009     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7010     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7011     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7013     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7014     GtkWidget *box = gtk_event_box_new ();
7015     gtk_container_add (GTK_CONTAINER (box), image);
7016     gtk_toolbar_append_widget( tbl, box, "", "");
7017     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7018     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7019     gtk_widget_hide (GTK_WIDGET (box));
7020     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7022     ////////////Size
7023     gchar const *const sizes[] = {
7024         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7025         "16", "18", "20", "22", "24", "28",
7026         "32", "36", "40", "48", "56", "64", "72", "144"
7027     };
7029     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7030     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7031         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7032     }
7033     gtk_widget_set_size_request (cbox, 80, -1);
7034     gtk_toolbar_append_widget( tbl, cbox, "", "");
7035     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7036     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7037     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7038     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7040     ////////////Text anchor
7041     GtkWidget *group   = gtk_radio_button_new (NULL);
7042     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7043     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7045     // left
7046     GtkWidget *rbutton = group;
7047     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7048     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7049     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7051     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7052     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7053     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7054     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7056     // center
7057     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7058     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7059     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7060     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7062     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7063     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7064     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7065     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7067     // right
7068     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7069     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7070     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7071     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7073     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7074     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7075     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7076     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7078     // fill
7079     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7080     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7081     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7082     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7084     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7085     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7086     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7087     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7089     gtk_toolbar_append_widget( tbl, row, "", "");
7091     //spacer
7092     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7094     ////////////Text style
7095     row = gtk_hbox_new (FALSE, 4);
7097     // bold
7098     rbutton = gtk_toggle_button_new ();
7099     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7100     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7101     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7102     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7104     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7105     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7106     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7108     // italic
7109     rbutton = gtk_toggle_button_new ();
7110     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7111     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7112     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7113     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7115     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7116     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7117     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7119     gtk_toolbar_append_widget( tbl, row, "", "");
7121     //spacer
7122     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7124     // Text orientation
7125     group   = gtk_radio_button_new (NULL);
7126     row     = gtk_hbox_new (FALSE, 4);
7127     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7129     // horizontal
7130     rbutton = group;
7131     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7132     gtk_container_add           (GTK_CONTAINER (rbutton),
7133                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7134     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7135     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7137     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7138     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7139     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7141     // vertical
7142     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7143     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7144     gtk_container_add           (GTK_CONTAINER (rbutton),
7145                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7146     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7147     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7149     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7150     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7151     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7152     gtk_toolbar_append_widget( tbl, row, "", "" );
7155     //watch selection
7156     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7158     sigc::connection *c_selection_changed =
7159         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7160                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7161     pool->add_connection ("selection-changed", c_selection_changed);
7163     sigc::connection *c_selection_modified =
7164         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7165                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7166     pool->add_connection ("selection-modified", c_selection_modified);
7168     sigc::connection *c_subselection_changed =
7169         new sigc::connection (desktop->connectToolSubselectionChanged
7170                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7171     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7173     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7176     gtk_widget_show_all( GTK_WIDGET(tbl) );
7178     return GTK_WIDGET(tbl);
7179 } // end of sp_text_toolbox_new()
7181 }//<unnamed> namespace
7184 //#########################
7185 //##      Connector      ##
7186 //#########################
7188 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7190     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7191     prefs->setBool("/tools/connector/mode",
7192                 gtk_toggle_action_get_active( act ));
7195 static void sp_connector_path_set_avoid(void)
7197     cc_selection_set_avoid(true);
7201 static void sp_connector_path_set_ignore(void)
7203     cc_selection_set_avoid(false);
7206 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7208     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7209     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7210     SPDocument *doc = sp_desktop_document(desktop);
7212     if (!sp_document_get_undo_sensitive(doc))
7213     {
7214         return;
7215     }
7218     // quit if run by the _changed callbacks
7219     if (g_object_get_data( tbl, "freeze" )) {
7220         return;
7221     }
7223     // in turn, prevent callbacks from responding
7224     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7226     bool is_orthog = gtk_toggle_action_get_active( act );
7227     gchar orthog_str[] = "orthogonal";
7228     gchar polyline_str[] = "polyline";
7229     gchar *value = is_orthog ? orthog_str : polyline_str ;
7231     bool modmade = false;
7232     GSList *l = (GSList *) selection->itemList();
7233     while (l) {
7234         SPItem *item = (SPItem *) l->data;
7236         if (cc_item_is_connector(item)) {
7237             sp_object_setAttribute(item, "inkscape:connector-type",
7238                     value, false);
7239             item->avoidRef->handleSettingChange();
7240             modmade = true;
7241         }
7242         l = l->next;
7243     }
7245     if (!modmade)
7246     {
7247         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7248         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7249     }
7251     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7252             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7254     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7257 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7259     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7260     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7261     SPDocument *doc = sp_desktop_document(desktop);
7263     if (!sp_document_get_undo_sensitive(doc))
7264     {
7265         return;
7266     }
7269     // quit if run by the _changed callbacks
7270     if (g_object_get_data( tbl, "freeze" )) {
7271         return;
7272     }
7274     // in turn, prevent callbacks from responding
7275     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7277     gdouble newValue = gtk_adjustment_get_value(adj);
7278     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7279     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7281     bool modmade = false;
7282     GSList *l = (GSList *) selection->itemList();
7283     while (l) {
7284         SPItem *item = (SPItem *) l->data;
7286         if (cc_item_is_connector(item)) {
7287             sp_object_setAttribute(item, "inkscape:connector-curvature",
7288                     value, false);
7289             item->avoidRef->handleSettingChange();
7290             modmade = true;
7291         }
7292         l = l->next;
7293     }
7295     if (!modmade)
7296     {
7297         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7298         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7299     }
7301     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7302             _("Change connector curvature"));
7304     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7308 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7310     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7311     SPDocument *doc = sp_desktop_document(desktop);
7313     if (!sp_document_get_undo_sensitive(doc))
7314     {
7315         return;
7316     }
7318     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7320     if ( !repr->attribute("inkscape:connector-spacing") &&
7321             ( adj->value == defaultConnSpacing )) {
7322         // Don't need to update the repr if the attribute doesn't
7323         // exist and it is being set to the default value -- as will
7324         // happen at startup.
7325         return;
7326     }
7328     // quit if run by the attr_changed listener
7329     if (g_object_get_data( tbl, "freeze" )) {
7330         return;
7331     }
7333     // in turn, prevent listener from responding
7334     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7336     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7337     SP_OBJECT(desktop->namedview)->updateRepr();
7339     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7340     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7341         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7342         Geom::Matrix m = Geom::identity();
7343         avoid_item_move(&m, item);
7344     }
7346     if (items) {
7347         g_slist_free(items);
7348     }
7350     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7351             _("Change connector spacing"));
7353     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7356 static void sp_connector_graph_layout(void)
7358     if (!SP_ACTIVE_DESKTOP) return;
7359     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7361     // hack for clones, see comment in align-and-distribute.cpp
7362     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7363     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7365     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7367     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7369     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7372 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7374     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7375     prefs->setBool("/tools/connector/directedlayout",
7376                 gtk_toggle_action_get_active( act ));
7379 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7381     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7382     prefs->setBool("/tools/connector/avoidoverlaplayout",
7383                 gtk_toggle_action_get_active( act ));
7387 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7389     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7390     prefs->setDouble("/tools/connector/length", adj->value);
7393 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7394                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7395                                             bool /*is_interactive*/, gpointer data)
7397     GtkWidget *tbl = GTK_WIDGET(data);
7399     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7400         return;
7401     }
7402     if (strcmp(name, "inkscape:connector-spacing") == 0)
7403     {
7404         GtkAdjustment *adj = (GtkAdjustment*)
7405                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7406         gdouble spacing = defaultConnSpacing;
7407         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7409         gtk_adjustment_set_value(adj, spacing);
7410         gtk_adjustment_value_changed(adj);
7411     }
7413     spinbutton_defocus(GTK_OBJECT(tbl));
7416 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7418     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7419     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7421     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7422         cc_create_connection_point(cc);
7425 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7427     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7428     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7430     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7431         cc_remove_connection_point(cc);
7434 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7435     NULL, /* child_added */
7436     NULL, /* child_removed */
7437     connector_tb_event_attr_changed,
7438     NULL, /* content_changed */
7439     NULL  /* order_changed */
7440 };
7442 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7444     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7445     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7446     SPItem *item = selection->singleItem();
7447     if (SP_IS_PATH(item))
7448     {
7449         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7450         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7451         gtk_toggle_action_set_active(act, is_orthog);
7452         gtk_adjustment_set_value(adj, curvature);
7453     }
7457 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7459     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7460     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7462     // Editing mode toggle button
7463     {
7464         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7465                                                       _("EditMode"),
7466                                                       _("Switch between connection point editing and connector drawing mode"),
7467                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7468                                                       Inkscape::ICON_SIZE_DECORATION );
7469         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7471         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7472         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7473         g_object_set_data( holder, "mode", act );
7474         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7475     }
7478     {
7479         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7480                                           _("Avoid"),
7481                                           _("Make connectors avoid selected objects"),
7482                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7483                                           secondarySize );
7484         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7485         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7486     }
7488     {
7489         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7490                                           _("Ignore"),
7491                                           _("Make connectors ignore selected objects"),
7492                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7493                                           secondarySize );
7494         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7495         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7496     }
7498     // Orthogonal connectors toggle button
7499     {
7500         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7501                                                       _("Orthogonal"),
7502                                                       _("Make connector orthogonal or polyline"),
7503                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7504                                                       Inkscape::ICON_SIZE_DECORATION );
7505         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7507         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7508         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7509         g_object_set_data( holder, "orthogonal", act );
7510         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7511     }
7513     EgeAdjustmentAction* eact = 0;
7514     // Curvature spinbox
7515     eact = create_adjustment_action( "ConnectorCurvatureAction",
7516                                     _("Connector Curvature"), _("Curvature:"),
7517                                     _("The amount of connectors curvature"),
7518                                     "/tools/connector/curvature", defaultConnCurvature,
7519                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7520                                     0, 100, 1.0, 10.0,
7521                                     0, 0, 0,
7522                                     connector_curvature_changed, 1, 0 );
7523     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7525     // Spacing spinbox
7526     eact = create_adjustment_action( "ConnectorSpacingAction",
7527                                     _("Connector Spacing"), _("Spacing:"),
7528                                     _("The amount of space left around objects by auto-routing connectors"),
7529                                     "/tools/connector/spacing", defaultConnSpacing,
7530                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7531                                     0, 100, 1.0, 10.0,
7532                                     0, 0, 0,
7533                                     connector_spacing_changed, 1, 0 );
7534     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7536     // Graph (connector network) layout
7537     {
7538         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7539                                           _("Graph"),
7540                                           _("Nicely arrange selected connector network"),
7541                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7542                                           secondarySize );
7543         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7544         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7545     }
7547     // Default connector length spinbox
7548     eact = create_adjustment_action( "ConnectorLengthAction",
7549                                      _("Connector Length"), _("Length:"),
7550                                      _("Ideal length for connectors when layout is applied"),
7551                                      "/tools/connector/length", 100,
7552                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7553                                      10, 1000, 10.0, 100.0,
7554                                      0, 0, 0,
7555                                      connector_length_changed, 1, 0 );
7556     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7559     // Directed edges toggle button
7560     {
7561         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7562                                                       _("Downwards"),
7563                                                       _("Make connectors with end-markers (arrows) point downwards"),
7564                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7565                                                       Inkscape::ICON_SIZE_DECORATION );
7566         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7568         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7569         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7571         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7572         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7573         );
7574     }
7576     // Avoid overlaps toggle button
7577     {
7578         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7579                                                       _("Remove overlaps"),
7580                                                       _("Do not allow overlapping shapes"),
7581                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7582                                                       Inkscape::ICON_SIZE_DECORATION );
7583         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7585         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7586         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7588         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7589     }
7592     // New connection point button
7593     {
7594         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7595                                           _("New connection point"),
7596                                           _("Add a new connection point to the currently selected item"),
7597                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7598                                           secondarySize );
7599         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7600         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7601     }
7603     // Remove selected connection point button
7605     {
7606         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7607                                           _("Remove connection point"),
7608                                           _("Remove the currently selected connection point"),
7609                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7610                                           secondarySize );
7611         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7612         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7613     }
7616     // Code to watch for changes to the connector-spacing attribute in
7617     // the XML.
7618     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7619     g_assert(repr != NULL);
7621     purge_repr_listener( holder, holder );
7623     if (repr) {
7624         g_object_set_data( holder, "repr", repr );
7625         Inkscape::GC::anchor(repr);
7626         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7627         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7628     }
7629 } // end of sp_connector_toolbox_prep()
7632 //#########################
7633 //##     Paintbucket     ##
7634 //#########################
7636 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7638     gint channels = ege_select_one_action_get_active( act );
7639     flood_channels_set_channels( channels );
7642 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7644     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7645     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7648 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7650     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7651     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7654 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7656     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7657     SPUnit const *unit = tracker->getActiveUnit();
7658     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7660     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7661     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7664 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7666     // FIXME: make defaults settable via Inkscape Options
7667     struct KeyValue {
7668         char const *key;
7669         double value;
7670     } const key_values[] = {
7671         {"threshold", 15},
7672         {"offset", 0.0}
7673     };
7675     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7676         KeyValue const &kv = key_values[i];
7677         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7678         if ( adj ) {
7679             gtk_adjustment_set_value(adj, kv.value);
7680         }
7681     }
7683     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7684     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7685     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7686     ege_select_one_action_set_active( autogap_action, 0 );
7689 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7691     EgeAdjustmentAction* eact = 0;
7692     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7694     {
7695         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7697         GList* items = 0;
7698         gint count = 0;
7699         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7700         {
7701             GtkTreeIter iter;
7702             gtk_list_store_append( model, &iter );
7703             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7704             count++;
7705         }
7706         g_list_free( items );
7707         items = 0;
7708         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7709         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7710         ege_select_one_action_set_appearance( act1, "compact" );
7711         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7712         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7713         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7714         g_object_set_data( holder, "channels_action", act1 );
7715     }
7717     // Spacing spinbox
7718     {
7719         eact = create_adjustment_action(
7720             "ThresholdAction",
7721             _("Fill Threshold"), _("Threshold:"),
7722             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7723             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7724             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7725             0, 0, 0,
7726             paintbucket_threshold_changed, 1, 0 );
7728         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7729         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7730     }
7732     // Create the units menu.
7733     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7734     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7735     if (!stored_unit.empty())
7736         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7737     g_object_set_data( holder, "tracker", tracker );
7738     {
7739         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7740         gtk_action_group_add_action( mainActions, act );
7741     }
7743     // Offset spinbox
7744     {
7745         eact = create_adjustment_action(
7746             "OffsetAction",
7747             _("Grow/shrink by"), _("Grow/shrink by:"),
7748             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7749             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7750             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7751             0, 0, 0,
7752             paintbucket_offset_changed, 1, 2);
7753         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7755         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7756     }
7758     /* Auto Gap */
7759     {
7760         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7762         GList* items = 0;
7763         gint count = 0;
7764         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7765         {
7766             GtkTreeIter iter;
7767             gtk_list_store_append( model, &iter );
7768             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7769             count++;
7770         }
7771         g_list_free( items );
7772         items = 0;
7773         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7774         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7775         ege_select_one_action_set_appearance( act2, "compact" );
7776         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7777         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7778         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7779         g_object_set_data( holder, "autogap_action", act2 );
7780     }
7782     /* Reset */
7783     {
7784         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7785                                           _("Defaults"),
7786                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7787                                           GTK_STOCK_CLEAR );
7788         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7789         gtk_action_group_add_action( mainActions, act );
7790         gtk_action_set_sensitive( act, TRUE );
7791     }
7795 /*
7796   Local Variables:
7797   mode:c++
7798   c-file-style:"stroustrup"
7799   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7800   indent-tabs-mode:nil
7801   fill-column:99
7802   End:
7803 */
7804 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :