Code

Spray Tool refactoring and enhanced distribution algorithm
[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         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4565         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4566         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4567         g_object_set_data( holder, "spray_population", eact );
4568     }
4570     /* Use Pressure button */
4571     {
4572         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4573                                                       _("Pressure"),
4574                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4575                                                       "use_pressure",
4576                                                       Inkscape::ICON_SIZE_DECORATION );
4577         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4578         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4579         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4580     }
4582     {   /* Rotation */
4583         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4584         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4585         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4586                                                               _("Rotation"), _("Rotation:"),
4587                                                               // xgettext:no-c-format
4588                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4589                                                               "/tools/spray/rotation_variation", 0,
4590                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4591                                                               0, 100, 1.0, 10.0,
4592                                                               labels, values, G_N_ELEMENTS(labels),
4593                                                               sp_spray_rotation_value_changed,  1, 0 );
4594         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4595         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4596         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4597         g_object_set_data( holder, "spray_rotation", eact );
4598     }
4600     {   /* Scale */
4601         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4602         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4603         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4604                                                               _("Scale"), _("Scale:"),
4605                                                               // xgettext:no-c-format
4606                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4607                                                               "/tools/spray/scale_variation", 0,
4608                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4609                                                               0, 100, 1.0, 10.0,
4610                                                               labels, values, G_N_ELEMENTS(labels),
4611                                                               sp_spray_scale_value_changed,  1, 0 );
4612         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4613         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4614         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4615         g_object_set_data( holder, "spray_scale", eact );
4616     }
4623 //########################
4624 //##     Calligraphy    ##
4625 //########################
4626 static void update_presets_list (GObject *tbl)
4628     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4629     if (g_object_get_data(tbl, "presets_blocked"))
4630         return;
4632     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4633     if (!sel) {
4634         // WTF!? This will cause a segfault if ever reached
4635         //ege_select_one_action_set_active(sel, 0);
4636         return;
4637     }
4639     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4641     int ege_index = 1;
4642     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4643         bool match = true;
4645         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4646         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4647             Glib::ustring entry_name = j->getEntryName();
4648             if (entry_name == "id" || entry_name == "name") continue;
4650             void *widget = g_object_get_data(tbl, entry_name.data());
4651             if (widget) {
4652                 if (GTK_IS_ADJUSTMENT(widget)) {
4653                     double v = j->getDouble();
4654                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4655                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4656                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4657                         match = false;
4658                         break;
4659                     }
4660                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4661                     bool v = j->getBool();
4662                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4663                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4664                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4665                         match = false;
4666                         break;
4667                     }
4668                 }
4669             }
4670         }
4672         if (match) {
4673             // newly added item is at the same index as the
4674             // save command, so we need to change twice for it to take effect
4675             ege_select_one_action_set_active(sel, 0);
4676             ege_select_one_action_set_active(sel, ege_index); // one-based index
4677             return;
4678         }
4679     }
4681     // no match found
4682     ege_select_one_action_set_active(sel, 0);
4685 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4687     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4688     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4689     update_presets_list(tbl);
4692 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4694     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4695     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4696     update_presets_list(tbl);
4699 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4701     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4702     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4703     update_presets_list(tbl);
4706 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4708     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4709     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4710     update_presets_list(tbl);
4713 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4715     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4716     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4717     update_presets_list(tbl);
4720 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4722     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4723     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4724     update_presets_list(tbl);
4727 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4729     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4730     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4731     update_presets_list(tbl);
4734 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4736     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4737     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4738     update_presets_list(tbl);
4741 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4743     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4744     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4745     update_presets_list(tbl);
4748 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4750     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4751     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4752     update_presets_list(tbl);
4755 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4757     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4758     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4759     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4760     update_presets_list(tbl);
4761     if (calligraphy_angle )
4762         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4766 static gchar const *const widget_names[] = {
4767     "width",
4768     "mass",
4769     "wiggle",
4770     "angle",
4771     "thinning",
4772     "tremor",
4773     "flatness",
4774     "cap_rounding",
4775     "usepressure",
4776     "tracebackground",
4777     "usetilt"
4778 };
4781 static void sp_dcc_build_presets_list(GObject *tbl)
4783     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4785     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4786     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4787     gtk_list_store_clear (model);
4789     {
4790         GtkTreeIter iter;
4791         gtk_list_store_append( model, &iter );
4792         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4793     }
4795     // iterate over all presets to populate the list
4796     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4797     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4798     int ii=1;
4800     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4801         GtkTreeIter iter;
4802         Glib::ustring preset_name = prefs->getString(*i + "/name");
4803         gtk_list_store_append( model, &iter );
4804         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4805     }
4807     {
4808         GtkTreeIter iter;
4809         gtk_list_store_append( model, &iter );
4810         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4811         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4812     }
4814     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4816     update_presets_list (tbl);
4819 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4821     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4822     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4823     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4824     if (! desktop) return;
4826     if (g_object_get_data(tbl, "presets_blocked"))
4827         return;
4829     CalligraphicProfileRename::show(desktop);
4830     if ( !CalligraphicProfileRename::applied()) {
4831         // dialog cancelled
4832         update_presets_list (tbl);
4833         return;
4834     }
4835     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4837     if (profile_name.empty()) {
4838         // empty name entered
4839         update_presets_list (tbl);
4840         return;
4841     }
4843     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4845     // If there's a preset with the given name, find it and set save_path appropriately
4846     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4847     int total_presets = presets.size();
4848     int new_index = -1;
4849     Glib::ustring save_path; // profile pref path without a trailing slash
4851     int temp_index = 0;
4852     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4853         Glib::ustring name = prefs->getString(*i + "/name");
4854         if (!name.empty() && profile_name == name) {
4855             new_index = temp_index;
4856             save_path = *i;
4857             break;
4858         }
4859     }
4861     if (new_index == -1) {
4862         // no preset with this name, create
4863         new_index = total_presets + 1;
4864         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4865         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4866         g_free(profile_id);
4867     }
4869     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4870         gchar const *const widget_name = widget_names[i];
4871         void *widget = g_object_get_data(tbl, widget_name);
4872         if (widget) {
4873             if (GTK_IS_ADJUSTMENT(widget)) {
4874                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4875                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4876                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4877             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4878                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4879                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4880                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4881             } else {
4882                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4883             }
4884         } else {
4885             g_warning("Bad key when writing preset: %s\n", widget_name);
4886         }
4887     }
4888     prefs->setString(save_path + "/name", profile_name);
4890     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4891     sp_dcc_build_presets_list (tbl);
4895 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4897     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4899     gint preset_index = ege_select_one_action_get_active( act );
4900     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4901     // even when the preset is not changed. It would be good to replace it with something more
4902     // modern. Index 0 means "No preset", so we don't do anything.
4903     if (preset_index == 0) return;
4905     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4907     if (preset_index == save_presets_index) {
4908         // this is the Save command
4909         sp_dcc_save_profile(NULL, tbl);
4910         return;
4911     }
4913     if (g_object_get_data(tbl, "presets_blocked"))
4914         return;
4916     // preset_index is one-based so we subtract 1
4917     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4918     Glib::ustring preset_path = presets.at(preset_index - 1);
4920     if (!preset_path.empty()) {
4921         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
4923         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4925         // Shouldn't this be std::map?
4926         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4927             Glib::ustring entry_name = i->getEntryName();
4928             if (entry_name == "id" || entry_name == "name") continue;
4929             void *widget = g_object_get_data(tbl, entry_name.data());
4930             if (widget) {
4931                 if (GTK_IS_ADJUSTMENT(widget)) {
4932                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4933                     gtk_adjustment_set_value(adj, i->getDouble());
4934                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4935                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4936                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4937                     gtk_toggle_action_set_active(toggle, i->getBool());
4938                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4939                 } else {
4940                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4941                 }
4942             } else {
4943                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4944             }
4945         }
4946         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4947     }
4951 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4953     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4954     {
4955         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4957         EgeAdjustmentAction* calligraphy_angle = 0;
4959         {
4960         /* Width */
4961         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4962         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4963         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4964                                                               _("Pen Width"), _("Width:"),
4965                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4966                                                               "/tools/calligraphic/width", 15,
4967                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4968                                                               1, 100, 1.0, 10.0,
4969                                                               labels, values, G_N_ELEMENTS(labels),
4970                                                               sp_ddc_width_value_changed,  1, 0 );
4971         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4972         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4973         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4974         }
4976         {
4977         /* Thinning */
4978             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4979             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4980         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4981                                                               _("Stroke Thinning"), _("Thinning:"),
4982                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4983                                                               "/tools/calligraphic/thinning", 10,
4984                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4985                                                               -100, 100, 1, 10.0,
4986                                                               labels, values, G_N_ELEMENTS(labels),
4987                                                               sp_ddc_velthin_value_changed, 1, 0);
4988         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4989         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4990         }
4992         {
4993         /* Angle */
4994         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4995         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4996         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4997                                                               _("Pen Angle"), _("Angle:"),
4998                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4999                                                               "/tools/calligraphic/angle", 30,
5000                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5001                                                               -90.0, 90.0, 1.0, 10.0,
5002                                                               labels, values, G_N_ELEMENTS(labels),
5003                                                               sp_ddc_angle_value_changed, 1, 0 );
5004         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5005         g_object_set_data( holder, "angle_action", eact );
5006         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5007         calligraphy_angle = eact;
5008         }
5010         {
5011         /* Fixation */
5012             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5013         gdouble values[] = {0, 20, 40, 60, 90, 100};
5014         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5015                                                               _("Fixation"), _("Fixation:"),
5016                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5017                                                               "/tools/calligraphic/flatness", 90,
5018                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5019                                                               0.0, 100, 1.0, 10.0,
5020                                                               labels, values, G_N_ELEMENTS(labels),
5021                                                               sp_ddc_flatness_value_changed, 1, 0);
5022         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5023         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5024         }
5026         {
5027         /* Cap Rounding */
5028             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5029         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5030         // TRANSLATORS: "cap" means "end" (both start and finish) here
5031         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5032                                                               _("Cap rounding"), _("Caps:"),
5033                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5034                                                               "/tools/calligraphic/cap_rounding", 0.0,
5035                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5036                                                               0.0, 5.0, 0.01, 0.1,
5037                                                               labels, values, G_N_ELEMENTS(labels),
5038                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5039         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5040         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5041         }
5043         {
5044         /* Tremor */
5045             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5046         gdouble values[] = {0, 10, 20, 40, 60, 100};
5047         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5048                                                               _("Stroke Tremor"), _("Tremor:"),
5049                                                               _("Increase to make strokes rugged and trembling"),
5050                                                               "/tools/calligraphic/tremor", 0.0,
5051                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5052                                                               0.0, 100, 1, 10.0,
5053                                                               labels, values, G_N_ELEMENTS(labels),
5054                                                               sp_ddc_tremor_value_changed, 1, 0);
5056         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5057         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5058         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5059         }
5061         {
5062         /* Wiggle */
5063         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5064         gdouble values[] = {0, 20, 40, 60, 100};
5065         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5066                                                               _("Pen Wiggle"), _("Wiggle:"),
5067                                                               _("Increase to make the pen waver and wiggle"),
5068                                                               "/tools/calligraphic/wiggle", 0.0,
5069                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5070                                                               0.0, 100, 1, 10.0,
5071                                                               labels, values, G_N_ELEMENTS(labels),
5072                                                               sp_ddc_wiggle_value_changed, 1, 0);
5073         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5074         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5075         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5076         }
5078         {
5079         /* Mass */
5080             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5081         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5082         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5083                                                               _("Pen Mass"), _("Mass:"),
5084                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5085                                                               "/tools/calligraphic/mass", 2.0,
5086                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5087                                                               0.0, 100, 1, 10.0,
5088                                                               labels, values, G_N_ELEMENTS(labels),
5089                                                               sp_ddc_mass_value_changed, 1, 0);
5090         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5091         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5092         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5093         }
5096         /* Trace Background button */
5097         {
5098             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5099                                                           _("Trace Background"),
5100                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5101                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5102                                                           Inkscape::ICON_SIZE_DECORATION );
5103             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5104             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5105             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5106             g_object_set_data( holder, "tracebackground", act );
5107         }
5109         /* Use Pressure button */
5110         {
5111             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5112                                                           _("Pressure"),
5113                                                           _("Use the pressure of the input device to alter the width of the pen"),
5114                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5115                                                           Inkscape::ICON_SIZE_DECORATION );
5116             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5117             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5118             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5119             g_object_set_data( holder, "usepressure", act );
5120         }
5122         /* Use Tilt button */
5123         {
5124             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5125                                                           _("Tilt"),
5126                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5127                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5128                                                           Inkscape::ICON_SIZE_DECORATION );
5129             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5130             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5131             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5132             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5133             g_object_set_data( holder, "usetilt", act );
5134         }
5136         /*calligraphic profile */
5137         {
5138             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5139             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5140             ege_select_one_action_set_appearance (act1, "compact");
5141             g_object_set_data (holder, "profile_selector", act1 );
5143             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5145             sp_dcc_build_presets_list (holder);
5147             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5148             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5149         }
5150     }
5154 //########################
5155 //##    Circle / Arc    ##
5156 //########################
5158 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5160     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5161     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5163     if (v1 == 0 && v2 == 0) {
5164         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5165             gtk_action_set_sensitive( ocb, FALSE );
5166             gtk_action_set_sensitive( make_whole, FALSE );
5167         }
5168     } else {
5169         gtk_action_set_sensitive( ocb, TRUE );
5170         gtk_action_set_sensitive( make_whole, TRUE );
5171     }
5174 static void
5175 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5177     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5179     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5180         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5181         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5182     }
5184     // quit if run by the attr_changed listener
5185     if (g_object_get_data( tbl, "freeze" )) {
5186         return;
5187     }
5189     // in turn, prevent listener from responding
5190     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5192     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5194     bool modmade = false;
5195     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5196          items != NULL;
5197          items = items->next)
5198     {
5199         SPItem *item = SP_ITEM(items->data);
5201         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5203             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5204             SPArc *arc = SP_ARC(item);
5206             if (!strcmp(value_name, "start"))
5207                 ge->start = (adj->value * M_PI)/ 180;
5208             else
5209                 ge->end = (adj->value * M_PI)/ 180;
5211             sp_genericellipse_normalize(ge);
5212             ((SPObject *)arc)->updateRepr();
5213             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5215             modmade = true;
5216         }
5217     }
5219     g_free(namespaced_name);
5221     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5223     sp_arctb_sensitivize( tbl, adj->value, other->value );
5225     if (modmade) {
5226         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5227                                    _("Arc: Change start/end"));
5228     }
5230     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5234 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5236     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5239 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5241     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5245 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5247     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5248     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5249         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5250         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5251     }
5253     // quit if run by the attr_changed listener
5254     if (g_object_get_data( tbl, "freeze" )) {
5255         return;
5256     }
5258     // in turn, prevent listener from responding
5259     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5261     bool modmade = false;
5263     if ( ege_select_one_action_get_active(act) != 0 ) {
5264         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5265              items != NULL;
5266              items = items->next)
5267         {
5268             if (SP_IS_ARC((SPItem *) items->data)) {
5269                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5270                 repr->setAttribute("sodipodi:open", "true");
5271                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5272                 modmade = true;
5273             }
5274         }
5275     } else {
5276         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5277              items != NULL;
5278              items = items->next)
5279         {
5280             if (SP_IS_ARC((SPItem *) items->data))    {
5281                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5282                 repr->setAttribute("sodipodi:open", NULL);
5283                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5284                 modmade = true;
5285             }
5286         }
5287     }
5289     if (modmade) {
5290         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5291                                    _("Arc: Change open/closed"));
5292     }
5294     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5297 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5299     GtkAdjustment *adj;
5300     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5301     gtk_adjustment_set_value(adj, 0.0);
5302     gtk_adjustment_value_changed(adj);
5304     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5305     gtk_adjustment_set_value(adj, 0.0);
5306     gtk_adjustment_value_changed(adj);
5308     spinbutton_defocus( GTK_OBJECT(obj) );
5311 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5312                                       gchar const */*old_value*/, gchar const */*new_value*/,
5313                                       bool /*is_interactive*/, gpointer data)
5315     GObject *tbl = G_OBJECT(data);
5317     // quit if run by the _changed callbacks
5318     if (g_object_get_data( tbl, "freeze" )) {
5319         return;
5320     }
5322     // in turn, prevent callbacks from responding
5323     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5325     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5326     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5328     GtkAdjustment *adj1,*adj2;
5329     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5330     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5331     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5332     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5334     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5336     char const *openstr = NULL;
5337     openstr = repr->attribute("sodipodi:open");
5338     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5340     if (openstr) {
5341         ege_select_one_action_set_active( ocb, 1 );
5342     } else {
5343         ege_select_one_action_set_active( ocb, 0 );
5344     }
5346     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5349 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5350     NULL, /* child_added */
5351     NULL, /* child_removed */
5352     arc_tb_event_attr_changed,
5353     NULL, /* content_changed */
5354     NULL  /* order_changed */
5355 };
5358 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5360     int n_selected = 0;
5361     Inkscape::XML::Node *repr = NULL;
5363     purge_repr_listener( tbl, tbl );
5365     for (GSList const *items = selection->itemList();
5366          items != NULL;
5367          items = items->next)
5368     {
5369         if (SP_IS_ARC((SPItem *) items->data)) {
5370             n_selected++;
5371             repr = SP_OBJECT_REPR((SPItem *) items->data);
5372         }
5373     }
5375     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5377     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5378     if (n_selected == 0) {
5379         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5380     } else if (n_selected == 1) {
5381         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5382         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5384         if (repr) {
5385             g_object_set_data( tbl, "repr", repr );
5386             Inkscape::GC::anchor(repr);
5387             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5388             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5389         }
5390     } else {
5391         // FIXME: implement averaging of all parameters for multiple selected
5392         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5393         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5394         sp_arctb_sensitivize( tbl, 1, 0 );
5395     }
5399 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5401     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5403     EgeAdjustmentAction* eact = 0;
5404     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5407     {
5408         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5409         ege_output_action_set_use_markup( act, TRUE );
5410         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5411         g_object_set_data( holder, "mode_action", act );
5412     }
5414     /* Start */
5415     {
5416         eact = create_adjustment_action( "ArcStartAction",
5417                                          _("Start"), _("Start:"),
5418                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5419                                          "/tools/shapes/arc/start", 0.0,
5420                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5421                                          -360.0, 360.0, 1.0, 10.0,
5422                                          0, 0, 0,
5423                                          sp_arctb_start_value_changed);
5424         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5425     }
5427     /* End */
5428     {
5429         eact = create_adjustment_action( "ArcEndAction",
5430                                          _("End"), _("End:"),
5431                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5432                                          "/tools/shapes/arc/end", 0.0,
5433                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5434                                          -360.0, 360.0, 1.0, 10.0,
5435                                          0, 0, 0,
5436                                          sp_arctb_end_value_changed);
5437         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5438     }
5440     /* Segments / Pie checkbox */
5441     {
5442         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5444         GtkTreeIter iter;
5445         gtk_list_store_append( model, &iter );
5446         gtk_list_store_set( model, &iter,
5447                             0, _("Closed arc"),
5448                             1, _("Switch to segment (closed shape with two radii)"),
5449                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5450                             -1 );
5452         gtk_list_store_append( model, &iter );
5453         gtk_list_store_set( model, &iter,
5454                             0, _("Open Arc"),
5455                             1, _("Switch to arc (unclosed shape)"),
5456                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5457                             -1 );
5459         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5460         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5461         g_object_set_data( holder, "open_action", act );
5463         ege_select_one_action_set_appearance( act, "full" );
5464         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5465         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5466         ege_select_one_action_set_icon_column( act, 2 );
5467         ege_select_one_action_set_icon_size( act, secondarySize );
5468         ege_select_one_action_set_tooltip_column( act, 1  );
5470         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5471         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5472         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5473     }
5475     /* Make Whole */
5476     {
5477         InkAction* inky = ink_action_new( "ArcResetAction",
5478                                           _("Make whole"),
5479                                           _("Make the shape a whole ellipse, not arc or segment"),
5480                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5481                                           secondarySize );
5482         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5483         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5484         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5485         g_object_set_data( holder, "make_whole", inky );
5486     }
5488     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5489     // sensitivize make whole and open checkbox
5490     {
5491         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5492         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5493         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5494     }
5497     sigc::connection *connection = new sigc::connection(
5498         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5499         );
5500     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5501     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5507 // toggle button callbacks and updaters
5509 //########################
5510 //##      Dropper       ##
5511 //########################
5513 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5514     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5515     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5516     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5517     if ( set_action ) {
5518         if ( gtk_toggle_action_get_active( act ) ) {
5519             gtk_action_set_sensitive( set_action, TRUE );
5520         } else {
5521             gtk_action_set_sensitive( set_action, FALSE );
5522         }
5523     }
5525     spinbutton_defocus(GTK_OBJECT(tbl));
5528 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5529     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5530     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5531     spinbutton_defocus(GTK_OBJECT(tbl));
5535 /**
5536  * Dropper auxiliary toolbar construction and setup.
5537  *
5538  * TODO: Would like to add swatch of current color.
5539  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5540  *       can drag and drop places. Will provide a nice mixing palette.
5541  */
5542 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5544     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5545     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5547     {
5548         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5549         ege_output_action_set_use_markup( act, TRUE );
5550         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5551     }
5553     {
5554         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5555                                                       _("Pick opacity"),
5556                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5557                                                       NULL,
5558                                                       Inkscape::ICON_SIZE_DECORATION );
5559         g_object_set( act, "short_label", _("Pick"), NULL );
5560         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5561         g_object_set_data( holder, "pick_action", act );
5562         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5563         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5564     }
5566     {
5567         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5568                                                       _("Assign opacity"),
5569                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5570                                                       NULL,
5571                                                       Inkscape::ICON_SIZE_DECORATION );
5572         g_object_set( act, "short_label", _("Assign"), NULL );
5573         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5574         g_object_set_data( holder, "set_action", act );
5575         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5576         // make sure it's disabled if we're not picking alpha
5577         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5578         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5579     }
5583 //########################
5584 //##      LPETool       ##
5585 //########################
5587 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5589 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5590 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5592     using namespace Inkscape::LivePathEffect;
5594     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5595     SPEventContext *ec = desktop->event_context;
5596     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5597         return;
5598     }
5600     // only take action if run by the attr_changed listener
5601     if (!g_object_get_data(tbl, "freeze")) {
5602         // in turn, prevent listener from responding
5603         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5605         gint mode = ege_select_one_action_get_active(act);
5606         EffectType type = lpesubtools[mode].type;
5608         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5609         bool success = lpetool_try_construction(lc, type);
5610         if (success) {
5611             // since the construction was already performed, we set the state back to inactive
5612             ege_select_one_action_set_active(act, 0);
5613             mode = 0;
5614         } else {
5615             // switch to the chosen subtool
5616             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5617         }
5619         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5620             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5621             prefs->setInt( "/tools/lpetool/mode", mode );
5622         }
5624         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5625     }
5628 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5630     SPEventContext *ec = selection->desktop()->event_context;
5631     if (!SP_IS_LPETOOL_CONTEXT(ec))
5632         return;
5634     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5637 void
5638 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5640     using namespace Inkscape::LivePathEffect;
5641     SPEventContext *ec = selection->desktop()->event_context;
5642     if (!SP_IS_LPETOOL_CONTEXT(ec))
5643         return;
5644     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5646     lpetool_delete_measuring_items(lc);
5647     lpetool_create_measuring_items(lc, selection);
5649     // activate line segment combo box if a single item with LPELineSegment is selected
5650     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5651     SPItem *item = selection->singleItem();
5652     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5653         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5654         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5655         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5656             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5657             g_object_set_data(tbl, "currentlpe", lpe);
5658             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5659             gtk_action_set_sensitive(w, TRUE);
5660             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5661         } else {
5662             g_object_set_data(tbl, "currentlpe", NULL);
5663             g_object_set_data(tbl, "currentlpeitem", NULL);
5664             gtk_action_set_sensitive(w, FALSE);
5665         }
5666     } else {
5667         g_object_set_data(tbl, "currentlpe", NULL);
5668         g_object_set_data(tbl, "currentlpeitem", NULL);
5669         gtk_action_set_sensitive(w, FALSE);
5670     }
5673 static void
5674 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5675     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5676     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5678     bool show = gtk_toggle_action_get_active( act );
5679     prefs->setBool("/tools/lpetool/show_bbox",  show);
5681     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5682         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5683         lpetool_context_reset_limiting_bbox(lc);
5684     }
5687 static void
5688 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5689     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5690     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5691         return;
5693     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5694     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5695     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5696         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5697         bool show = gtk_toggle_action_get_active( act );
5698         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5699         lpetool_show_measuring_info(lc, show);
5700         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5701     }
5704 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5705     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5706     SPUnit const *unit = tracker->getActiveUnit();
5707     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5708     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5710     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5711     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5712         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5713         lpetool_delete_measuring_items(lc);
5714         lpetool_create_measuring_items(lc);
5715     }
5718 static void
5719 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5720     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5721     Inkscape::Selection *selection = desktop->selection;
5723     Geom::OptRect bbox = selection->bounds();
5725     if (bbox) {
5726         Geom::Point A(bbox->min());
5727         Geom::Point B(bbox->max());
5729         A *= desktop->doc2dt();
5730         B *= desktop->doc2dt();
5732         // TODO: should we provide a way to store points in prefs?
5733         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5734         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5735         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5736         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5737         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5739         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5740     }
5742     gtk_toggle_action_set_active(act, false);
5745 static void
5746 sp_line_segment_build_list(GObject *tbl)
5748     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5750     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5751     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5752     gtk_list_store_clear (model);
5754     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5755     {
5756         GtkTreeIter iter;
5757         gtk_list_store_append( model, &iter );
5758         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5759         gtk_list_store_append( model, &iter );
5760         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5761         gtk_list_store_append( model, &iter );
5762         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5763         gtk_list_store_append( model, &iter );
5764         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5765     }
5767     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5770 static void
5771 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5772     using namespace Inkscape::LivePathEffect;
5774     // quit if run by the attr_changed listener
5775     if (g_object_get_data(tbl, "freeze")) {
5776         return;
5777     }
5779     // in turn, prevent listener from responding
5780     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5782     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5783     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5784     if (lpeitem) {
5785         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5786         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5787         sp_lpe_item_update_patheffect(lpeitem, true, true);
5788     }
5790     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5793 static void
5794 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5795     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5797     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5798         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5799     }
5800     gtk_toggle_action_set_active(act, false);
5803 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5805     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5806     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5807     g_object_set_data(holder, "tracker", tracker);
5808     SPUnit const *unit = tracker->getActiveUnit();
5810     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5811     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5813     /** Automatically create a list of LPEs that get added to the toolbar **/
5814     {
5815         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5817         GtkTreeIter iter;
5819         // the first toggle button represents the state that no subtool is active (remove this when
5820         // this can be modeled by EgeSelectOneAction or some other action)
5821         gtk_list_store_append( model, &iter );
5822         gtk_list_store_set( model, &iter,
5823                             0, _("All inactive"),
5824                             1, _("No geometric tool is active"),
5825                             2, "draw-geometry-inactive",
5826                             -1 );
5828         Inkscape::LivePathEffect::EffectType type;
5829         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5830             type =  lpesubtools[i].type;
5831             gtk_list_store_append( model, &iter );
5832             gtk_list_store_set( model, &iter,
5833                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5834                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5835                                 2, lpesubtools[i].icon_name,
5836                                 -1 );
5837         }
5839         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5840         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5841         g_object_set_data( holder, "lpetool_mode_action", act );
5843         ege_select_one_action_set_appearance( act, "full" );
5844         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5845         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5846         ege_select_one_action_set_icon_column( act, 2 );
5847         ege_select_one_action_set_tooltip_column( act, 1  );
5849         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5850         ege_select_one_action_set_active( act, lpeToolMode );
5851         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5852     }
5854     /* Show limiting bounding box */
5855     {
5856         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5857                                                       _("Show limiting bounding box"),
5858                                                       _("Show bounding box (used to cut infinite lines)"),
5859                                                       "show-bounding-box",
5860                                                       Inkscape::ICON_SIZE_DECORATION );
5861         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5862         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5863         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5864     }
5866     /* Set limiting bounding box to bbox of current selection */
5867     {
5868         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5869                                                       _("Get limiting bounding box from selection"),
5870                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5871                                                       "draw-geometry-set-bounding-box",
5872                                                       Inkscape::ICON_SIZE_DECORATION );
5873         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5874         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5875         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5876     }
5879     /* Combo box to choose line segment type */
5880     {
5881         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5882         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5883         ege_select_one_action_set_appearance (act, "compact");
5884         g_object_set_data (holder, "lpetool_line_segment_action", act );
5886         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5888         sp_line_segment_build_list (holder);
5890         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5891         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5892         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5893     }
5895     /* Display measuring info for selected items */
5896     {
5897         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5898                                                       _("Display measuring info"),
5899                                                       _("Display measuring info for selected items"),
5900                                                       "draw-geometry-show-measuring-info",
5901                                                       Inkscape::ICON_SIZE_DECORATION );
5902         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5903         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5904         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5905     }
5907     // add the units menu
5908     {
5909         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5910         gtk_action_group_add_action( mainActions, act );
5911         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5912         g_object_set_data(holder, "lpetool_units_action", act);
5913         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5914     }
5916     /* Open LPE dialog (to adapt parameters numerically) */
5917     {
5918         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5919                                                       _("Open LPE dialog"),
5920                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5921                                                       "dialog-geometry",
5922                                                       Inkscape::ICON_SIZE_DECORATION );
5923         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5924         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5925         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5926     }
5928     //watch selection
5929     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5931     sigc::connection *c_selection_modified =
5932         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5933                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5934     pool->add_connection ("selection-modified", c_selection_modified);
5936     sigc::connection *c_selection_changed =
5937         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5938                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5939     pool->add_connection ("selection-changed", c_selection_changed);
5942 //########################
5943 //##       Eraser       ##
5944 //########################
5946 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5948     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5949     prefs->setDouble( "/tools/eraser/width", adj->value );
5950     update_presets_list(tbl);
5953 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5955     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5956     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5957     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5958         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5959         prefs->setBool( "/tools/eraser/mode", eraserMode );
5960     }
5962     // only take action if run by the attr_changed listener
5963     if (!g_object_get_data( tbl, "freeze" )) {
5964         // in turn, prevent listener from responding
5965         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5967         if ( eraserMode != 0 ) {
5968         } else {
5969         }
5970         // TODO finish implementation
5972         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5973     }
5976 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5978     {
5979         /* Width */
5980         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5981         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5982         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5983                                                               _("Pen Width"), _("Width:"),
5984                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5985                                                               "/tools/eraser/width", 15,
5986                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5987                                                               1, 100, 1.0, 10.0,
5988                                                               labels, values, G_N_ELEMENTS(labels),
5989                                                               sp_erc_width_value_changed, 1, 0);
5990         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5991         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5992         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5993     }
5995     {
5996         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5998         GtkTreeIter iter;
5999         gtk_list_store_append( model, &iter );
6000         gtk_list_store_set( model, &iter,
6001                             0, _("Delete"),
6002                             1, _("Delete objects touched by the eraser"),
6003                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6004                             -1 );
6006         gtk_list_store_append( model, &iter );
6007         gtk_list_store_set( model, &iter,
6008                             0, _("Cut"),
6009                             1, _("Cut out from objects"),
6010                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6011                             -1 );
6013         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6014         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6015         g_object_set_data( holder, "eraser_mode_action", act );
6017         ege_select_one_action_set_appearance( act, "full" );
6018         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6019         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6020         ege_select_one_action_set_icon_column( act, 2 );
6021         ege_select_one_action_set_tooltip_column( act, 1  );
6023         /// @todo Convert to boolean?
6024         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6025         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6026         ege_select_one_action_set_active( act, eraserMode );
6027         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6028     }
6032 //########################
6033 //##    Text Toolbox    ##
6034 //########################
6035 /*
6036 static void
6037 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6039     //Call back for letter sizing spinbutton
6042 static void
6043 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6045     //Call back for line height spinbutton
6048 static void
6049 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6051     //Call back for horizontal kerning spinbutton
6054 static void
6055 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6057     //Call back for vertical kerning spinbutton
6060 static void
6061 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6063     //Call back for letter rotation spinbutton
6064 }*/
6066 namespace {
6068 void
6069 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6071     // quit if run by the _changed callbacks
6072     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6073         return;
6074     }
6076     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6078     SPStyle *query =
6079         sp_style_new (SP_ACTIVE_DOCUMENT);
6081     int result_family =
6082         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6084     int result_style =
6085         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6087     int result_numbers =
6088         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6090     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6092     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6093     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6094         // there are no texts in selection, read from prefs
6096         sp_style_read_from_prefs(query, "/tools/text");
6098         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6099             // do not reset the toolbar style from prefs if we already did it last time
6100             sp_style_unref(query);
6101             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6102             return;
6103         }
6104         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6105     } else {
6106         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6107     }
6109     if (query->text)
6110     {
6111         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6112             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6113             gtk_entry_set_text (GTK_ENTRY (entry), "");
6115         } else if (query->text->font_specification.value || query->text->font_family.value) {
6117             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6118             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6120             // Get the font that corresponds
6121             Glib::ustring familyName;
6123             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6124             if (font) {
6125                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6126                 font->Unref();
6127                 font = NULL;
6128             }
6130             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6132             Gtk::TreeIter iter;
6133             try {
6134                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6135                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6136                 iter = model->get_iter(path);
6137             } catch (...) {
6138                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6139                 sp_style_unref(query);
6140                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6141                 return;
6142             }
6144             combo->set_active (iter);
6145         }
6147         //Size
6148         {
6149             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6150             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6151             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6152             g_free(str);
6153         }
6155         //Anchor
6156         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6157         {
6158             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6159             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6160             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6161             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6162         }
6163         else
6164         {
6165             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6166             {
6167                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6168                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6169                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6170                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6171             }
6172             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6173             {
6174                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6175                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6176                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6177                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6178             }
6179             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6180             {
6181                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6182                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6183                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6184                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6185             }
6186         }
6188         //Style
6189         {
6190             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6192             gboolean active = gtk_toggle_button_get_active (button);
6193             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));
6195             if (active != check)
6196             {
6197                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6198                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6199                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6200             }
6201         }
6203         {
6204             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6206             gboolean active = gtk_toggle_button_get_active (button);
6207             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6209             if (active != check)
6210             {
6211                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6212                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6213                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6214             }
6215         }
6217         //Orientation
6218         //locking both buttons, changing one affect all group (both)
6219         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6220         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6222         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6223         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6225         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6226         {
6227             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6228         }
6229         else
6230         {
6231             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6232         }
6233         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6234         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6235     }
6237     sp_style_unref(query);
6239     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6242 void
6243 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6245     sp_text_toolbox_selection_changed (selection, tbl);
6248 void
6249 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6251     sp_text_toolbox_selection_changed (NULL, tbl);
6254 void
6255 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6256                                 GObject             *tbl)
6258     // quit if run by the _changed callbacks
6259     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6260         return;
6261     }
6263     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6265     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6266     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6267     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6269     //g_print ("family changed to: %s\n", family);
6271     SPStyle *query =
6272         sp_style_new (SP_ACTIVE_DOCUMENT);
6274     int result_fontspec =
6275         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6277     SPCSSAttr *css = sp_repr_css_attr_new ();
6279     // First try to get the font spec from the stored value
6280     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6282     if (fontSpec.empty()) {
6283         // Construct a new font specification if it does not yet exist
6284         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6285         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6286         fontFromStyle->Unref();
6287     }
6289     if (!fontSpec.empty()) {
6291         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6293         if (!newFontSpec.empty()) {
6295             if (fontSpec != newFontSpec) {
6297                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6299                 if (font) {
6300                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6302                     // Set all the these just in case they were altered when finding the best
6303                     // match for the new family and old style...
6305                     gchar c[256];
6307                     font->Family(c, 256);
6309                     sp_repr_css_set_property (css, "font-family", c);
6311                     font->Attribute( "weight", c, 256);
6312                     sp_repr_css_set_property (css, "font-weight", c);
6314                     font->Attribute("style", c, 256);
6315                     sp_repr_css_set_property (css, "font-style", c);
6317                     font->Attribute("stretch", c, 256);
6318                     sp_repr_css_set_property (css, "font-stretch", c);
6320                     font->Attribute("variant", c, 256);
6321                     sp_repr_css_set_property (css, "font-variant", c);
6323                     font->Unref();
6324                 }
6325             }
6327         } else {
6328             // If the old font on selection (or default) was not existing on the system,
6329             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6330             // setting the family reported by the family chooser.
6332             //g_print ("fallback setting family: %s\n", family);
6333             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6334             sp_repr_css_set_property (css, "font-family", family);
6335         }
6336     }
6338     // If querying returned nothing, set the default style of the tool (for new texts)
6339     if (result_fontspec == QUERY_STYLE_NOTHING)
6340     {
6341         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6342         prefs->mergeStyle("/tools/text/style", css);
6343         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6344     }
6345     else
6346     {
6347         sp_desktop_set_style (desktop, css, true, true);
6348     }
6350     sp_style_unref(query);
6352     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6353                                    _("Text: Change font family"));
6354     sp_repr_css_attr_unref (css);
6356     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6358     // unfreeze
6359     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6361     // focus to canvas
6362     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6366 void
6367 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6368                                    gpointer          data)
6370     if (g_object_get_data (G_OBJECT (button), "block")) return;
6371     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6372     int prop = GPOINTER_TO_INT(data);
6374     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6376     // move the x of all texts to preserve the same bbox
6377     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6378     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6379         if (SP_IS_TEXT((SPItem *) items->data)) {
6380             SPItem *item = SP_ITEM(items->data);
6382             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6383             // below, variable names suggest horizontal move, but we check the writing direction
6384             // and move in the corresponding axis
6385             int axis;
6386             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6387                 axis = NR::X;
6388             } else {
6389                 axis = NR::Y;
6390             }
6392             Geom::OptRect bbox
6393                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6394             if (!bbox)
6395                 continue;
6396             double width = bbox->dimensions()[axis];
6397             // If you want to align within some frame, other than the text's own bbox, calculate
6398             // the left and right (or top and bottom for tb text) slacks of the text inside that
6399             // frame (currently unused)
6400             double left_slack = 0;
6401             double right_slack = 0;
6402             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6403             double move = 0;
6404             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6405                 switch (prop) {
6406                     case 0:
6407                         move = -left_slack;
6408                         break;
6409                     case 1:
6410                         move = width/2 + (right_slack - left_slack)/2;
6411                         break;
6412                     case 2:
6413                         move = width + right_slack;
6414                         break;
6415                 }
6416             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6417                 switch (prop) {
6418                     case 0:
6419                         move = -width/2 - left_slack;
6420                         break;
6421                     case 1:
6422                         move = (right_slack - left_slack)/2;
6423                         break;
6424                     case 2:
6425                         move = width/2 + right_slack;
6426                         break;
6427                 }
6428             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6429                 switch (prop) {
6430                     case 0:
6431                         move = -width - left_slack;
6432                         break;
6433                     case 1:
6434                         move = -width/2 + (right_slack - left_slack)/2;
6435                         break;
6436                     case 2:
6437                         move = right_slack;
6438                         break;
6439                 }
6440             }
6441             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6442             if (axis == NR::X) {
6443                 XY = XY + Geom::Point (move, 0);
6444             } else {
6445                 XY = XY + Geom::Point (0, move);
6446             }
6447             SP_TEXT(item)->attributes.setFirstXY(XY);
6448             SP_OBJECT(item)->updateRepr();
6449             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6450         }
6451     }
6453     SPCSSAttr *css = sp_repr_css_attr_new ();
6454     switch (prop)
6455     {
6456         case 0:
6457         {
6458             sp_repr_css_set_property (css, "text-anchor", "start");
6459             sp_repr_css_set_property (css, "text-align", "start");
6460             break;
6461         }
6462         case 1:
6463         {
6464             sp_repr_css_set_property (css, "text-anchor", "middle");
6465             sp_repr_css_set_property (css, "text-align", "center");
6466             break;
6467         }
6469         case 2:
6470         {
6471             sp_repr_css_set_property (css, "text-anchor", "end");
6472             sp_repr_css_set_property (css, "text-align", "end");
6473             break;
6474         }
6476         case 3:
6477         {
6478             sp_repr_css_set_property (css, "text-anchor", "start");
6479             sp_repr_css_set_property (css, "text-align", "justify");
6480             break;
6481         }
6482     }
6484     SPStyle *query =
6485         sp_style_new (SP_ACTIVE_DOCUMENT);
6486     int result_numbers =
6487         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6489     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6490     if (result_numbers == QUERY_STYLE_NOTHING)
6491     {
6492         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6493         prefs->mergeStyle("/tools/text/style", css);
6494     }
6496     sp_style_unref(query);
6498     sp_desktop_set_style (desktop, css, true, true);
6499     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6500                                    _("Text: Change alignment"));
6501     sp_repr_css_attr_unref (css);
6503     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6506 void
6507 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6508                                gpointer          data)
6510     if (g_object_get_data (G_OBJECT (button), "block")) return;
6512     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6513     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6514     int          prop       = GPOINTER_TO_INT(data);
6515     bool         active     = gtk_toggle_button_get_active (button);
6517     SPStyle *query =
6518         sp_style_new (SP_ACTIVE_DOCUMENT);
6520     int result_fontspec =
6521         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6523     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6524     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6525     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6527     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6528     Glib::ustring newFontSpec = "";
6530     if (fontSpec.empty()) {
6531         // Construct a new font specification if it does not yet exist
6532         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6533         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6534         fontFromStyle->Unref();
6535     }
6537     bool nochange = true;
6538     switch (prop)
6539     {
6540         case 0:
6541         {
6542             if (!fontSpec.empty()) {
6543                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6544                 if (!newFontSpec.empty()) {
6545                     // Don't even set the bold if the font didn't exist on the system
6546                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6547                     nochange = false;
6548                 }
6549             }
6550             // set or reset the button according
6551             if(nochange) {
6552                 gboolean check = gtk_toggle_button_get_active (button);
6554                 if (active != check)
6555                 {
6556                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6557                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6558                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6559                 }
6560             }
6562             break;
6563         }
6565         case 1:
6566         {
6567             if (!fontSpec.empty()) {
6568                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6569                 if (!newFontSpec.empty()) {
6570                     // Don't even set the italic if the font didn't exist on the system
6571                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6572                     nochange = false;
6573                 }
6574             }
6575             if(nochange) {
6576                 gboolean check = gtk_toggle_button_get_active (button);
6578                 if (active != check)
6579                 {
6580                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6581                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6582                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6583                 }
6584             }
6585             break;
6586         }
6587     }
6589     if (!newFontSpec.empty()) {
6590         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6591     }
6593     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6594     if (result_fontspec == QUERY_STYLE_NOTHING)
6595     {
6596         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6597         prefs->mergeStyle("/tools/text/style", css);
6598     }
6600     sp_style_unref(query);
6602     sp_desktop_set_style (desktop, css, true, true);
6603     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6604                                    _("Text: Change font style"));
6605     sp_repr_css_attr_unref (css);
6607     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6610 void
6611 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6612                                      gpointer         data)
6614     if (g_object_get_data (G_OBJECT (button), "block")) {
6615         return;
6616     }
6618     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6619     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6620     int          prop       = GPOINTER_TO_INT(data);
6622     switch (prop)
6623     {
6624         case 0:
6625         {
6626             sp_repr_css_set_property (css, "writing-mode", "lr");
6627             break;
6628         }
6630         case 1:
6631         {
6632             sp_repr_css_set_property (css, "writing-mode", "tb");
6633             break;
6634         }
6635     }
6637     SPStyle *query =
6638         sp_style_new (SP_ACTIVE_DOCUMENT);
6639     int result_numbers =
6640         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6642     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6643     if (result_numbers == QUERY_STYLE_NOTHING)
6644     {
6645         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6646         prefs->mergeStyle("/tools/text/style", css);
6647     }
6649     sp_desktop_set_style (desktop, css, true, true);
6650     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6651                                    _("Text: Change orientation"));
6652     sp_repr_css_attr_unref (css);
6654     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6657 gboolean
6658 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6660     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6661     if (!desktop) return FALSE;
6663     switch (get_group0_keyval (event)) {
6664         case GDK_KP_Enter: // chosen
6665         case GDK_Return:
6666             // unfreeze and update, which will defocus
6667             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6668             sp_text_toolbox_family_changed (NULL, tbl);
6669             return TRUE; // I consumed the event
6670             break;
6671         case GDK_Escape:
6672             // defocus
6673             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6674             return TRUE; // I consumed the event
6675             break;
6676     }
6677     return FALSE;
6680 gboolean
6681 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6683     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6684     if (!desktop) return FALSE;
6686     switch (get_group0_keyval (event)) {
6687         case GDK_KP_Enter:
6688         case GDK_Return:
6689         case GDK_Escape: // defocus
6690             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6691             return TRUE; // I consumed the event
6692             break;
6693         case GDK_w:
6694         case GDK_W:
6695             if (event->state & GDK_CONTROL_MASK) {
6696                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6697                 return TRUE; // I consumed the event
6698             }
6699             break;
6700     }
6701     return FALSE;
6705 void
6706 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6707                                GObject     *tbl)
6709      // quit if run by the _changed callbacks
6710     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6711         return;
6712     }
6714     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6716    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6718     // If this is not from selecting a size in the list (in which case get_active will give the
6719     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6720     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6721     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6722    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6723         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6724         return;
6725    }
6727     gdouble value = -1;
6728     {
6729         gchar *endptr;
6730         gchar *const text = gtk_combo_box_get_active_text(cbox);
6731         if (text) {
6732             value = g_strtod(text, &endptr);
6733             if (endptr == text) {  // Conversion failed, non-numeric input.
6734                 value = -1;
6735             }
6736             g_free(text);
6737         }
6738     }
6739     if (value <= 0) {
6740         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6741         return; // could not parse value
6742     }
6744     SPCSSAttr *css = sp_repr_css_attr_new ();
6745     Inkscape::CSSOStringStream osfs;
6746     osfs << value;
6747     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6749     SPStyle *query =
6750         sp_style_new (SP_ACTIVE_DOCUMENT);
6751     int result_numbers =
6752         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6754     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6755     if (result_numbers == QUERY_STYLE_NOTHING)
6756     {
6757         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6758         prefs->mergeStyle("/tools/text/style", css);
6759     }
6761     sp_style_unref(query);
6763     sp_desktop_set_style (desktop, css, true, true);
6764     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6765                                    _("Text: Change font size"));
6766     sp_repr_css_attr_unref (css);
6768     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6770     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6773 gboolean
6774 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6776     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6777     if (!desktop) return FALSE;
6779     if (!g_object_get_data (tbl, "esc-pressed")) {
6780         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6781         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6782         sp_text_toolbox_size_changed (cbox, tbl);
6783         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6784     }
6785     return FALSE; // I consumed the event
6789 gboolean
6790 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6792     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6793     if (!desktop) return FALSE;
6795     switch (get_group0_keyval (event)) {
6796         case GDK_Escape: // defocus
6797             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6798             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6799             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6800             return TRUE; // I consumed the event
6801             break;
6802         case GDK_Return: // defocus
6803         case GDK_KP_Enter:
6804             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6805             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6806             sp_text_toolbox_size_changed (cbox, tbl);
6807             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6808             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6809             return TRUE; // I consumed the event
6810             break;
6811     }
6812     return FALSE;
6815 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6816 // does not work!
6817 gboolean
6818 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6819                                  GdkEventFocus    */*event*/,
6820                                  GObject          *tbl)
6822     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6823     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6824     return FALSE;
6827 gboolean
6828 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6829                                  GdkEventFocus    */*event*/,
6830                                  GObject          *tbl)
6832     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6833     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6834     return FALSE;
6837 void
6838 cell_data_func  (GtkCellLayout */*cell_layout*/,
6839                  GtkCellRenderer   *cell,
6840                  GtkTreeModel      *tree_model,
6841                  GtkTreeIter       *iter,
6842                  gpointer           /*data*/)
6844     gchar *family;
6845     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6846     gchar *const family_escaped = g_markup_escape_text(family, -1);
6848     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6849     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6850     if (show_sample) {
6852         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6853         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6855     std::stringstream markup;
6856     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6857            << family_escaped << "'>" << sample_escaped << "</span>";
6858     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6860         g_free(sample_escaped);
6861     } else {
6862         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6863     }
6865     g_free(family);
6866     g_free(family_escaped);
6869 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6870                                                 GtkTreeModel       *model,
6871                                                 GtkTreeIter        *iter,
6872                                                 GObject            *tbl)
6874     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6875     // press Enter again after choosing a completion)
6876     gchar *family = 0;
6877     gtk_tree_model_get(model, iter, 0, &family, -1);
6879     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6880     gtk_entry_set_text (GTK_ENTRY (entry), family);
6882     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6883     sp_text_toolbox_family_changed (NULL, tbl);
6884     return TRUE;
6888 static void
6889 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6890     GtkEntry *entry;
6891     GtkEntryCompletion *completion;
6892     GtkTreeModel *model;
6894     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6895     completion = gtk_entry_completion_new();
6896     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6897     gtk_entry_completion_set_model(completion, model);
6898     gtk_entry_completion_set_text_column(completion, 0);
6899     gtk_entry_completion_set_inline_completion(completion, FALSE);
6900     gtk_entry_completion_set_inline_selection(completion, FALSE);
6901     gtk_entry_completion_set_popup_completion(completion, TRUE);
6902     gtk_entry_set_completion(entry, completion);
6904     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6906     g_object_unref(completion);
6909 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6910                                       void */*property*/,
6911                                       GObject *tbl)
6913   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6915   gboolean shown;
6916   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6917   if (shown) {
6918          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6919          //g_print("POP: notify: SHOWN\n");
6920   } else {
6921          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6923          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6924          // find out if the drop down was closed by Enter and if so, manually update (only
6925          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6926          GdkEvent *ev = gtk_get_current_event();
6927          if (ev) {
6928              //g_print ("ev type: %d\n", ev->type);
6929              if (ev->type == GDK_KEY_PRESS) {
6930                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6931                      case GDK_KP_Enter: // chosen
6932                      case GDK_Return:
6933                      {
6934                          // make sure the chosen one is inserted into the entry
6935                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6936                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6937                          GtkTreeIter iter;
6938                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6939                          if (has_active) {
6940                              gchar *family;
6941                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6942                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6943                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6944                          }
6946                          // update
6947                          sp_text_toolbox_family_changed (NULL, tbl);
6948                          break;
6949                      }
6950                  }
6951              }
6952          }
6954          // regardless of whether we updated, defocus the widget
6955          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6956          if (desktop)
6957              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6958          //g_print("POP: notify: HIDDEN\n");
6959   }
6962 GtkWidget*
6963 sp_text_toolbox_new (SPDesktop *desktop)
6965     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6966     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6968     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6969     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6971     GtkTooltips *tt = gtk_tooltips_new();
6973     ////////////Family
6974     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6975     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6977     gtk_rc_parse_string (
6978        "style \"dropdown-as-list-style\"\n"
6979        "{\n"
6980        "    GtkComboBox::appears-as-list = 1\n"
6981        "}\n"
6982        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6983     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6984     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6986     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6988     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6990     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6991     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6993     // expand the field a bit so as to view more of the previews in the drop-down
6994     GtkRequisition req;
6995     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6996     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6998     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6999     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7001     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7002     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7003              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7004     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7005     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7006     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7008     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7009     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7011     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7012     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7013     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7014     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7016     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7017     GtkWidget *box = gtk_event_box_new ();
7018     gtk_container_add (GTK_CONTAINER (box), image);
7019     gtk_toolbar_append_widget( tbl, box, "", "");
7020     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7021     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7022     gtk_widget_hide (GTK_WIDGET (box));
7023     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7025     ////////////Size
7026     gchar const *const sizes[] = {
7027         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7028         "16", "18", "20", "22", "24", "28",
7029         "32", "36", "40", "48", "56", "64", "72", "144"
7030     };
7032     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7033     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7034         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7035     }
7036     gtk_widget_set_size_request (cbox, 80, -1);
7037     gtk_toolbar_append_widget( tbl, cbox, "", "");
7038     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7039     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7040     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7041     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7043     ////////////Text anchor
7044     GtkWidget *group   = gtk_radio_button_new (NULL);
7045     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7046     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7048     // left
7049     GtkWidget *rbutton = group;
7050     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7051     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7052     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7054     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7055     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7056     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7057     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7059     // center
7060     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7061     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7062     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7063     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7065     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7066     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7067     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7068     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7070     // right
7071     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7072     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7073     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7074     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7076     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7077     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7078     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7079     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7081     // fill
7082     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7083     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7084     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7085     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7087     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7088     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7089     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7090     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7092     gtk_toolbar_append_widget( tbl, row, "", "");
7094     //spacer
7095     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7097     ////////////Text style
7098     row = gtk_hbox_new (FALSE, 4);
7100     // bold
7101     rbutton = gtk_toggle_button_new ();
7102     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7103     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7104     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7105     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7107     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7108     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7109     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7111     // italic
7112     rbutton = gtk_toggle_button_new ();
7113     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7114     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7115     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7116     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7118     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7119     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7120     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7122     gtk_toolbar_append_widget( tbl, row, "", "");
7124     //spacer
7125     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7127     // Text orientation
7128     group   = gtk_radio_button_new (NULL);
7129     row     = gtk_hbox_new (FALSE, 4);
7130     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7132     // horizontal
7133     rbutton = group;
7134     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7135     gtk_container_add           (GTK_CONTAINER (rbutton),
7136                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7137     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7138     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7140     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7141     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7142     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7144     // vertical
7145     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7146     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7147     gtk_container_add           (GTK_CONTAINER (rbutton),
7148                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7149     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7150     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7152     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7153     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7154     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7155     gtk_toolbar_append_widget( tbl, row, "", "" );
7158     //watch selection
7159     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7161     sigc::connection *c_selection_changed =
7162         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7163                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7164     pool->add_connection ("selection-changed", c_selection_changed);
7166     sigc::connection *c_selection_modified =
7167         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7168                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7169     pool->add_connection ("selection-modified", c_selection_modified);
7171     sigc::connection *c_subselection_changed =
7172         new sigc::connection (desktop->connectToolSubselectionChanged
7173                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7174     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7176     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7179     gtk_widget_show_all( GTK_WIDGET(tbl) );
7181     return GTK_WIDGET(tbl);
7182 } // end of sp_text_toolbox_new()
7184 }//<unnamed> namespace
7187 //#########################
7188 //##      Connector      ##
7189 //#########################
7191 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7193     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7194     prefs->setBool("/tools/connector/mode",
7195                 gtk_toggle_action_get_active( act ));
7198 static void sp_connector_path_set_avoid(void)
7200     cc_selection_set_avoid(true);
7204 static void sp_connector_path_set_ignore(void)
7206     cc_selection_set_avoid(false);
7209 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7211     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7212     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7213     SPDocument *doc = sp_desktop_document(desktop);
7215     if (!sp_document_get_undo_sensitive(doc))
7216     {
7217         return;
7218     }
7221     // quit if run by the _changed callbacks
7222     if (g_object_get_data( tbl, "freeze" )) {
7223         return;
7224     }
7226     // in turn, prevent callbacks from responding
7227     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7229     bool is_orthog = gtk_toggle_action_get_active( act );
7230     gchar orthog_str[] = "orthogonal";
7231     gchar polyline_str[] = "polyline";
7232     gchar *value = is_orthog ? orthog_str : polyline_str ;
7234     bool modmade = false;
7235     GSList *l = (GSList *) selection->itemList();
7236     while (l) {
7237         SPItem *item = (SPItem *) l->data;
7239         if (cc_item_is_connector(item)) {
7240             sp_object_setAttribute(item, "inkscape:connector-type",
7241                     value, false);
7242             item->avoidRef->handleSettingChange();
7243             modmade = true;
7244         }
7245         l = l->next;
7246     }
7248     if (!modmade)
7249     {
7250         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7251         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7252     }
7254     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7255             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7257     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7260 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7262     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7263     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7264     SPDocument *doc = sp_desktop_document(desktop);
7266     if (!sp_document_get_undo_sensitive(doc))
7267     {
7268         return;
7269     }
7272     // quit if run by the _changed callbacks
7273     if (g_object_get_data( tbl, "freeze" )) {
7274         return;
7275     }
7277     // in turn, prevent callbacks from responding
7278     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7280     gdouble newValue = gtk_adjustment_get_value(adj);
7281     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7282     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7284     bool modmade = false;
7285     GSList *l = (GSList *) selection->itemList();
7286     while (l) {
7287         SPItem *item = (SPItem *) l->data;
7289         if (cc_item_is_connector(item)) {
7290             sp_object_setAttribute(item, "inkscape:connector-curvature",
7291                     value, false);
7292             item->avoidRef->handleSettingChange();
7293             modmade = true;
7294         }
7295         l = l->next;
7296     }
7298     if (!modmade)
7299     {
7300         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7301         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7302     }
7304     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7305             _("Change connector curvature"));
7307     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7311 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7313     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7314     SPDocument *doc = sp_desktop_document(desktop);
7316     if (!sp_document_get_undo_sensitive(doc))
7317     {
7318         return;
7319     }
7321     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7323     if ( !repr->attribute("inkscape:connector-spacing") &&
7324             ( adj->value == defaultConnSpacing )) {
7325         // Don't need to update the repr if the attribute doesn't
7326         // exist and it is being set to the default value -- as will
7327         // happen at startup.
7328         return;
7329     }
7331     // quit if run by the attr_changed listener
7332     if (g_object_get_data( tbl, "freeze" )) {
7333         return;
7334     }
7336     // in turn, prevent listener from responding
7337     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7339     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7340     SP_OBJECT(desktop->namedview)->updateRepr();
7342     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7343     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7344         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7345         Geom::Matrix m = Geom::identity();
7346         avoid_item_move(&m, item);
7347     }
7349     if (items) {
7350         g_slist_free(items);
7351     }
7353     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7354             _("Change connector spacing"));
7356     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7359 static void sp_connector_graph_layout(void)
7361     if (!SP_ACTIVE_DESKTOP) return;
7362     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7364     // hack for clones, see comment in align-and-distribute.cpp
7365     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7366     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7368     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7370     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7372     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7375 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7377     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7378     prefs->setBool("/tools/connector/directedlayout",
7379                 gtk_toggle_action_get_active( act ));
7382 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7384     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7385     prefs->setBool("/tools/connector/avoidoverlaplayout",
7386                 gtk_toggle_action_get_active( act ));
7390 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7392     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7393     prefs->setDouble("/tools/connector/length", adj->value);
7396 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7397                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7398                                             bool /*is_interactive*/, gpointer data)
7400     GtkWidget *tbl = GTK_WIDGET(data);
7402     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7403         return;
7404     }
7405     if (strcmp(name, "inkscape:connector-spacing") == 0)
7406     {
7407         GtkAdjustment *adj = (GtkAdjustment*)
7408                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7409         gdouble spacing = defaultConnSpacing;
7410         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7412         gtk_adjustment_set_value(adj, spacing);
7413         gtk_adjustment_value_changed(adj);
7414     }
7416     spinbutton_defocus(GTK_OBJECT(tbl));
7419 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7421     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7422     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7424     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7425         cc_create_connection_point(cc);
7428 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7430     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7431     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7433     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7434         cc_remove_connection_point(cc);
7437 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7438     NULL, /* child_added */
7439     NULL, /* child_removed */
7440     connector_tb_event_attr_changed,
7441     NULL, /* content_changed */
7442     NULL  /* order_changed */
7443 };
7445 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7447     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7448     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7449     SPItem *item = selection->singleItem();
7450     if (SP_IS_PATH(item))
7451     {
7452         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7453         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7454         gtk_toggle_action_set_active(act, is_orthog);
7455         gtk_adjustment_set_value(adj, curvature);
7456     }
7460 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7462     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7463     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7465     // Editing mode toggle button
7466     {
7467         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7468                                                       _("EditMode"),
7469                                                       _("Switch between connection point editing and connector drawing mode"),
7470                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7471                                                       Inkscape::ICON_SIZE_DECORATION );
7472         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7474         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7475         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7476         g_object_set_data( holder, "mode", act );
7477         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7478     }
7481     {
7482         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7483                                           _("Avoid"),
7484                                           _("Make connectors avoid selected objects"),
7485                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7486                                           secondarySize );
7487         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7488         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7489     }
7491     {
7492         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7493                                           _("Ignore"),
7494                                           _("Make connectors ignore selected objects"),
7495                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7496                                           secondarySize );
7497         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7498         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7499     }
7501     // Orthogonal connectors toggle button
7502     {
7503         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7504                                                       _("Orthogonal"),
7505                                                       _("Make connector orthogonal or polyline"),
7506                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7507                                                       Inkscape::ICON_SIZE_DECORATION );
7508         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7510         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7511         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7512         g_object_set_data( holder, "orthogonal", act );
7513         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7514     }
7516     EgeAdjustmentAction* eact = 0;
7517     // Curvature spinbox
7518     eact = create_adjustment_action( "ConnectorCurvatureAction",
7519                                     _("Connector Curvature"), _("Curvature:"),
7520                                     _("The amount of connectors curvature"),
7521                                     "/tools/connector/curvature", defaultConnCurvature,
7522                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7523                                     0, 100, 1.0, 10.0,
7524                                     0, 0, 0,
7525                                     connector_curvature_changed, 1, 0 );
7526     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7528     // Spacing spinbox
7529     eact = create_adjustment_action( "ConnectorSpacingAction",
7530                                     _("Connector Spacing"), _("Spacing:"),
7531                                     _("The amount of space left around objects by auto-routing connectors"),
7532                                     "/tools/connector/spacing", defaultConnSpacing,
7533                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7534                                     0, 100, 1.0, 10.0,
7535                                     0, 0, 0,
7536                                     connector_spacing_changed, 1, 0 );
7537     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7539     // Graph (connector network) layout
7540     {
7541         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7542                                           _("Graph"),
7543                                           _("Nicely arrange selected connector network"),
7544                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7545                                           secondarySize );
7546         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7547         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7548     }
7550     // Default connector length spinbox
7551     eact = create_adjustment_action( "ConnectorLengthAction",
7552                                      _("Connector Length"), _("Length:"),
7553                                      _("Ideal length for connectors when layout is applied"),
7554                                      "/tools/connector/length", 100,
7555                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7556                                      10, 1000, 10.0, 100.0,
7557                                      0, 0, 0,
7558                                      connector_length_changed, 1, 0 );
7559     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7562     // Directed edges toggle button
7563     {
7564         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7565                                                       _("Downwards"),
7566                                                       _("Make connectors with end-markers (arrows) point downwards"),
7567                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7568                                                       Inkscape::ICON_SIZE_DECORATION );
7569         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7571         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7572         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7574         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7575         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7576         );
7577     }
7579     // Avoid overlaps toggle button
7580     {
7581         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7582                                                       _("Remove overlaps"),
7583                                                       _("Do not allow overlapping shapes"),
7584                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7585                                                       Inkscape::ICON_SIZE_DECORATION );
7586         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7588         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7589         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7591         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7592     }
7595     // New connection point button
7596     {
7597         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7598                                           _("New connection point"),
7599                                           _("Add a new connection point to the currently selected item"),
7600                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7601                                           secondarySize );
7602         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7603         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7604     }
7606     // Remove selected connection point button
7608     {
7609         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7610                                           _("Remove connection point"),
7611                                           _("Remove the currently selected connection point"),
7612                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7613                                           secondarySize );
7614         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7615         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7616     }
7619     // Code to watch for changes to the connector-spacing attribute in
7620     // the XML.
7621     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7622     g_assert(repr != NULL);
7624     purge_repr_listener( holder, holder );
7626     if (repr) {
7627         g_object_set_data( holder, "repr", repr );
7628         Inkscape::GC::anchor(repr);
7629         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7630         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7631     }
7632 } // end of sp_connector_toolbox_prep()
7635 //#########################
7636 //##     Paintbucket     ##
7637 //#########################
7639 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7641     gint channels = ege_select_one_action_get_active( act );
7642     flood_channels_set_channels( channels );
7645 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7647     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7648     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7651 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7653     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7654     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7657 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7659     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7660     SPUnit const *unit = tracker->getActiveUnit();
7661     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7663     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7664     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7667 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7669     // FIXME: make defaults settable via Inkscape Options
7670     struct KeyValue {
7671         char const *key;
7672         double value;
7673     } const key_values[] = {
7674         {"threshold", 15},
7675         {"offset", 0.0}
7676     };
7678     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7679         KeyValue const &kv = key_values[i];
7680         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7681         if ( adj ) {
7682             gtk_adjustment_set_value(adj, kv.value);
7683         }
7684     }
7686     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7687     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7688     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7689     ege_select_one_action_set_active( autogap_action, 0 );
7692 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7694     EgeAdjustmentAction* eact = 0;
7695     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7697     {
7698         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7700         GList* items = 0;
7701         gint count = 0;
7702         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7703         {
7704             GtkTreeIter iter;
7705             gtk_list_store_append( model, &iter );
7706             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7707             count++;
7708         }
7709         g_list_free( items );
7710         items = 0;
7711         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7712         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7713         ege_select_one_action_set_appearance( act1, "compact" );
7714         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7715         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7716         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7717         g_object_set_data( holder, "channels_action", act1 );
7718     }
7720     // Spacing spinbox
7721     {
7722         eact = create_adjustment_action(
7723             "ThresholdAction",
7724             _("Fill Threshold"), _("Threshold:"),
7725             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7726             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7727             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7728             0, 0, 0,
7729             paintbucket_threshold_changed, 1, 0 );
7731         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7732         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7733     }
7735     // Create the units menu.
7736     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7737     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7738     if (!stored_unit.empty())
7739         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7740     g_object_set_data( holder, "tracker", tracker );
7741     {
7742         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7743         gtk_action_group_add_action( mainActions, act );
7744     }
7746     // Offset spinbox
7747     {
7748         eact = create_adjustment_action(
7749             "OffsetAction",
7750             _("Grow/shrink by"), _("Grow/shrink by:"),
7751             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7752             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7753             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7754             0, 0, 0,
7755             paintbucket_offset_changed, 1, 2);
7756         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7758         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7759     }
7761     /* Auto Gap */
7762     {
7763         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7765         GList* items = 0;
7766         gint count = 0;
7767         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7768         {
7769             GtkTreeIter iter;
7770             gtk_list_store_append( model, &iter );
7771             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7772             count++;
7773         }
7774         g_list_free( items );
7775         items = 0;
7776         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7777         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7778         ege_select_one_action_set_appearance( act2, "compact" );
7779         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7780         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7781         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7782         g_object_set_data( holder, "autogap_action", act2 );
7783     }
7785     /* Reset */
7786     {
7787         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7788                                           _("Defaults"),
7789                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7790                                           GTK_STOCK_CLEAR );
7791         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7792         gtk_action_group_add_action( mainActions, act );
7793         gtk_action_set_sensitive( act, TRUE );
7794     }
7798 /*
7799   Local Variables:
7800   mode:c++
7801   c-file-style:"stroustrup"
7802   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7803   indent-tabs-mode:nil
7804   fill-column:99
7805   End:
7806 */
7807 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :