Code

48e22d53f2895d742f6807836f8de9ba11eddfe1
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@users.sf.net>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *
16  * Copyright (C) 2004 David Turner
17  * Copyright (C) 2003 MenTaLguY
18  * Copyright (C) 1999-2008 authors
19  * Copyright (C) 2001-2002 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../node-context.h"
69 #include "../pen-context.h"
70 #include "../preferences.h"
71 #include "../selection-chemistry.h"
72 #include "../selection.h"
73 #include "select-toolbar.h"
74 #include "../shape-editor.h"
75 #include "../shortcuts.h"
76 #include "../sp-clippath.h"
77 #include "../sp-ellipse.h"
78 #include "../sp-flowtext.h"
79 #include "../sp-mask.h"
80 #include "../sp-namedview.h"
81 #include "../sp-rect.h"
82 #include "../sp-spiral.h"
83 #include "../sp-star.h"
84 #include "../sp-text.h"
85 #include "../style.h"
86 #include "../svg/css-ostringstream.h"
87 #include "../tools-switch.h"
88 #include "../tweak-context.h"
89 #include "../spray-context.h"
90 #include "../ui/dialog/calligraphic-profile-rename.h"
91 #include "../ui/icon-names.h"
92 #include "../ui/widget/style-swatch.h"
93 #include "../verbs.h"
94 #include "../widgets/button.h"
95 #include "../widgets/spinbutton-events.h"
96 #include "../widgets/spw-utilities.h"
97 #include "../widgets/widget-sizes.h"
98 #include "../xml/attribute-record.h"
99 #include "../xml/node-event-vector.h"
100 #include "../xml/repr.h"
102 #include "toolbox.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
123 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
131 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
132     static Inkscape::IconSize sizeChoices[] = {
133         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
134         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
135         Inkscape::ICON_SIZE_MENU
136     };
137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
138     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
139     return sizeChoices[index];
142 static struct {
143     gchar const *type_name;
144     gchar const *data_name;
145     sp_verb_t verb;
146     sp_verb_t doubleclick_verb;
147 } const tools[] = {
148     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
149     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
150     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
151     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
152     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
153     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
154     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
155     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
156     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
157     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
158     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
159     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
160     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
161     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
162     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
163     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
164     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
165     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
166     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
167     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
168     { NULL, NULL, 0, 0 }
169 };
171 static struct {
172     gchar const *type_name;
173     gchar const *data_name;
174     GtkWidget *(*create_func)(SPDesktop *desktop);
175     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
176     gchar const *ui_name;
177     gint swatch_verb_id;
178     gchar const *swatch_tool;
179     gchar const *swatch_tip;
180 } const aux_toolboxes[] = {
181     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
182       SP_VERB_INVALID, 0, 0},
183     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
184       SP_VERB_INVALID, 0, 0},
185     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
186       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
187     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
188       SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
189     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
190       SP_VERB_INVALID, 0, 0},
191     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
192       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
193     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
194       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
195     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
196       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
197     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
198       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
199     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
200       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
201     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
202       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
203     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
204       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
205     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
206       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
207     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
208       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
209     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
210       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
211     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
212       SP_VERB_INVALID, 0, 0},
213     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
214       SP_VERB_INVALID, 0, 0},
215     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
216       SP_VERB_INVALID, 0, 0},
217     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
218       SP_VERB_INVALID, 0, 0},
219     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
221     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227         "<ui>"
228         "  <toolbar name='SelectToolbar'>"
229         "    <toolitem action='EditSelectAll' />"
230         "    <toolitem action='EditSelectAllInAllLayers' />"
231         "    <toolitem action='EditDeselect' />"
232         "    <separator />"
233         "    <toolitem action='ObjectRotate90CCW' />"
234         "    <toolitem action='ObjectRotate90' />"
235         "    <toolitem action='ObjectFlipHorizontally' />"
236         "    <toolitem action='ObjectFlipVertically' />"
237         "    <separator />"
238         "    <toolitem action='SelectionToBack' />"
239         "    <toolitem action='SelectionLower' />"
240         "    <toolitem action='SelectionRaise' />"
241         "    <toolitem action='SelectionToFront' />"
242         "    <separator />"
243         "    <toolitem action='XAction' />"
244         "    <toolitem action='YAction' />"
245         "    <toolitem action='WidthAction' />"
246         "    <toolitem action='LockAction' />"
247         "    <toolitem action='HeightAction' />"
248         "    <toolitem action='UnitsAction' />"
249         "    <separator />"
250         "    <toolitem action='transform_affect_label' />"
251         "    <toolitem action='transform_stroke' />"
252         "    <toolitem action='transform_corners' />"
253         "    <toolitem action='transform_gradient' />"
254         "    <toolitem action='transform_pattern' />"
255         "  </toolbar>"
257         "  <toolbar name='NodeToolbar'>"
258         "    <toolitem action='NodeInsertAction' />"
259         "    <toolitem action='NodeDeleteAction' />"
260         "    <separator />"
261         "    <toolitem action='NodeJoinAction' />"
262         "    <toolitem action='NodeBreakAction' />"
263         "    <separator />"
264         "    <toolitem action='NodeJoinSegmentAction' />"
265         "    <toolitem action='NodeDeleteSegmentAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeCuspAction' />"
268         "    <toolitem action='NodeSmoothAction' />"
269         "    <toolitem action='NodeSymmetricAction' />"
270         "    <toolitem action='NodeAutoAction' />"
271         "    <separator />"
272         "    <toolitem action='NodeLineAction' />"
273         "    <toolitem action='NodeCurveAction' />"
274         "    <separator />"
275         "    <toolitem action='ObjectToPath' />"
276         "    <toolitem action='StrokeToPath' />"
277         "    <separator />"
278         "    <toolitem action='NodeXAction' />"
279         "    <toolitem action='NodeYAction' />"
280         "    <toolitem action='NodeUnitsAction' />"
281         "    <separator />"
282         "    <toolitem action='ObjectEditClipPathAction' />"
283         "    <toolitem action='ObjectEditMaskPathAction' />"
284         "    <toolitem action='EditNextLPEParameterAction' />"
285         "    <separator />"
286         "    <toolitem action='NodesShowHandlesAction' />"
287         "    <toolitem action='NodesShowHelperpath' />"
288         "  </toolbar>"
290         "  <toolbar name='TweakToolbar'>"
291         "    <toolitem action='TweakWidthAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakForceAction' />"
294         "    <toolitem action='TweakPressureAction' />"
295         "    <separator />"
296         "    <toolitem action='TweakModeAction' />"
297         "    <separator />"
298         "    <toolitem action='TweakFidelityAction' />"
299         "    <separator />"
300         "    <toolitem action='TweakChannelsLabel' />"
301         "    <toolitem action='TweakDoH' />"
302         "    <toolitem action='TweakDoS' />"
303         "    <toolitem action='TweakDoL' />"
304         "    <toolitem action='TweakDoO' />"
305         "  </toolbar>"
307   "  <toolbar name='SprayToolbar'>"
308         "    <toolitem action='SprayModeAction' />"
309         "    <separator />"
310         "    <toolitem action='SprayWidthAction' />"
311         "    <separator />"
312         "    <toolitem action='SprayPressureAction' />"
313         "    <separator />"
314         "    <toolitem action='SprayPopulationAction' />"
315         "    <separator />"
316         "    <toolitem action='SprayMeanAction' />"
317         "    <toolitem action='SprayStandard_deviationAction' />"
318         "    <separator />"
319         "    <toolitem action='DialogSprayOption' />"
320         "  </toolbar>"
322         "  <toolbar name='ZoomToolbar'>"
323         "    <toolitem action='ZoomIn' />"
324         "    <toolitem action='ZoomOut' />"
325         "    <separator />"
326         "    <toolitem action='Zoom1:0' />"
327         "    <toolitem action='Zoom1:2' />"
328         "    <toolitem action='Zoom2:1' />"
329         "    <separator />"
330         "    <toolitem action='ZoomSelection' />"
331         "    <toolitem action='ZoomDrawing' />"
332         "    <toolitem action='ZoomPage' />"
333         "    <toolitem action='ZoomPageWidth' />"
334         "    <separator />"
335         "    <toolitem action='ZoomPrev' />"
336         "    <toolitem action='ZoomNext' />"
337         "  </toolbar>"
339         "  <toolbar name='StarToolbar'>"
340         "    <separator />"
341         "    <toolitem action='StarStateAction' />"
342         "    <separator />"
343         "    <toolitem action='FlatAction' />"
344         "    <separator />"
345         "    <toolitem action='MagnitudeAction' />"
346         "    <toolitem action='SpokeAction' />"
347         "    <toolitem action='RoundednessAction' />"
348         "    <toolitem action='RandomizationAction' />"
349         "    <separator />"
350         "    <toolitem action='StarResetAction' />"
351         "  </toolbar>"
353         "  <toolbar name='RectToolbar'>"
354         "    <toolitem action='RectStateAction' />"
355         "    <toolitem action='RectWidthAction' />"
356         "    <toolitem action='RectHeightAction' />"
357         "    <toolitem action='RadiusXAction' />"
358         "    <toolitem action='RadiusYAction' />"
359         "    <toolitem action='RectUnitsAction' />"
360         "    <separator />"
361         "    <toolitem action='RectResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='3DBoxToolbar'>"
365         "    <toolitem action='3DBoxAngleXAction' />"
366         "    <toolitem action='3DBoxVPXStateAction' />"
367         "    <separator />"
368         "    <toolitem action='3DBoxAngleYAction' />"
369         "    <toolitem action='3DBoxVPYStateAction' />"
370         "    <separator />"
371         "    <toolitem action='3DBoxAngleZAction' />"
372         "    <toolitem action='3DBoxVPZStateAction' />"
373         "  </toolbar>"
375         "  <toolbar name='SpiralToolbar'>"
376         "    <toolitem action='SpiralStateAction' />"
377         "    <toolitem action='SpiralRevolutionAction' />"
378         "    <toolitem action='SpiralExpansionAction' />"
379         "    <toolitem action='SpiralT0Action' />"
380         "    <separator />"
381         "    <toolitem action='SpiralResetAction' />"
382         "  </toolbar>"
384         "  <toolbar name='PenToolbar'>"
385         "    <toolitem action='FreehandModeActionPen' />"
386         "    <separator />"
387         "    <toolitem action='SetPenShapeAction'/>"
388         "  </toolbar>"
390         "  <toolbar name='PencilToolbar'>"
391         "    <toolitem action='FreehandModeActionPencil' />"
392         "    <separator />"
393         "    <toolitem action='PencilToleranceAction' />"
394         "    <separator />"
395         "    <toolitem action='PencilResetAction' />"
396         "    <separator />"
397         "    <toolitem action='SetPencilShapeAction'/>"
398         "  </toolbar>"
400         "  <toolbar name='CalligraphyToolbar'>"
401         "    <separator />"
402         "    <toolitem action='SetProfileAction'/>"
403         "    <separator />"
404         "    <toolitem action='CalligraphyWidthAction' />"
405         "    <toolitem action='PressureAction' />"
406         "    <toolitem action='TraceAction' />"
407         "    <toolitem action='ThinningAction' />"
408         "    <separator />"
409         "    <toolitem action='AngleAction' />"
410         "    <toolitem action='TiltAction' />"
411         "    <toolitem action='FixationAction' />"
412         "    <separator />"
413         "    <toolitem action='CapRoundingAction' />"
414         "    <separator />"
415         "    <toolitem action='TremorAction' />"
416         "    <toolitem action='WiggleAction' />"
417         "    <toolitem action='MassAction' />"
418         "    <separator />"
419         "  </toolbar>"
421         "  <toolbar name='ArcToolbar'>"
422         "    <toolitem action='ArcStateAction' />"
423         "    <separator />"
424         "    <toolitem action='ArcStartAction' />"
425         "    <toolitem action='ArcEndAction' />"
426         "    <separator />"
427         "    <toolitem action='ArcOpenAction' />"
428         "    <separator />"
429         "    <toolitem action='ArcResetAction' />"
430         "    <separator />"
431         "  </toolbar>"
433         "  <toolbar name='PaintbucketToolbar'>"
434         "    <toolitem action='ChannelsAction' />"
435         "    <separator />"
436         "    <toolitem action='ThresholdAction' />"
437         "    <separator />"
438         "    <toolitem action='OffsetAction' />"
439         "    <toolitem action='PaintbucketUnitsAction' />"
440         "    <separator />"
441         "    <toolitem action='AutoGapAction' />"
442         "    <separator />"
443         "    <toolitem action='PaintbucketResetAction' />"
444         "  </toolbar>"
446         "  <toolbar name='EraserToolbar'>"
447         "    <toolitem action='EraserWidthAction' />"
448         "    <separator />"
449         "    <toolitem action='EraserModeAction' />"
450         "  </toolbar>"
452         "  <toolbar name='LPEToolToolbar'>"
453         "    <toolitem action='LPEToolModeAction' />"
454         "    <separator />"
455         "    <toolitem action='LPEShowBBoxAction' />"
456         "    <toolitem action='LPEBBoxFromSelectionAction' />"
457         "    <separator />"
458         "    <toolitem action='LPELineSegmentAction' />"
459         "    <separator />"
460         "    <toolitem action='LPEMeasuringAction' />"
461         "    <toolitem action='LPEToolUnitsAction' />"
462         "    <separator />"
463         "    <toolitem action='LPEOpenLPEDialogAction' />"
464         "  </toolbar>"
466         "  <toolbar name='DropperToolbar'>"
467         "    <toolitem action='DropperOpacityAction' />"
468         "    <toolitem action='DropperPickAlphaAction' />"
469         "    <toolitem action='DropperSetAlphaAction' />"
470         "  </toolbar>"
472         "  <toolbar name='ConnectorToolbar'>"
473         "    <toolitem action='ConnectorEditModeAction' />"
474         "    <toolitem action='ConnectorAvoidAction' />"
475         "    <toolitem action='ConnectorIgnoreAction' />"
476         "    <toolitem action='ConnectorOrthogonalAction' />"
477         "    <toolitem action='ConnectorCurvatureAction' />"
478         "    <toolitem action='ConnectorSpacingAction' />"
479         "    <toolitem action='ConnectorGraphAction' />"
480         "    <toolitem action='ConnectorLengthAction' />"
481         "    <toolitem action='ConnectorDirectedAction' />"
482         "    <toolitem action='ConnectorOverlapAction' />"
483         "    <toolitem action='ConnectorNewConnPointAction' />"
484         "    <toolitem action='ConnectorRemoveConnPointAction' />"
485         "  </toolbar>"
487         "</ui>"
490 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
492 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
494 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
495 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
497 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
498 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
500 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
501 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
503 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
504                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
505                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
507 class VerbAction : public Gtk::Action {
508 public:
509     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511     virtual ~VerbAction();
512     virtual void set_active(bool active = true);
514 protected:
515     virtual Gtk::Widget* create_menu_item_vfunc();
516     virtual Gtk::Widget* create_tool_item_vfunc();
518     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
519     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
521     virtual void on_activate();
523 private:
524     Inkscape::Verb* verb;
525     Inkscape::Verb* verb2;
526     Inkscape::UI::View::View *view;
527     GtkTooltips *tooltips;
528     bool active;
530     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
531 };
534 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
536     Glib::RefPtr<VerbAction> result;
537     SPAction *action = verb->get_action(view);
538     if ( action ) {
539         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
540         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
541     }
543     return result;
546 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
547     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
548     verb(verb),
549     verb2(verb2),
550     view(view),
551     tooltips(tooltips),
552     active(false)
556 VerbAction::~VerbAction()
560 Gtk::Widget* VerbAction::create_menu_item_vfunc()
562 // First call in to get the icon rendered if present in SVG
563     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
564     delete widget;
565     widget = 0;
567     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
568 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
569     return widg;
572 Gtk::Widget* VerbAction::create_tool_item_vfunc()
574 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
575     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
576     GtkWidget* toolbox = 0;
577     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
578                                                                           SP_BUTTON_TYPE_TOGGLE,
579                                                                           verb,
580                                                                           verb2,
581                                                                           view,
582                                                                           tooltips );
583     if ( active ) {
584         sp_button_toggle_set_down( SP_BUTTON(button), active);
585     }
586     gtk_widget_show_all( button );
587     Gtk::Widget* wrapped = Glib::wrap(button);
588     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
589     holder->add(*wrapped);
591 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
592     return holder;
595 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
597 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
598     Gtk::Action::connect_proxy_vfunc(proxy);
601 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
603 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
604     Gtk::Action::disconnect_proxy_vfunc(proxy);
607 void VerbAction::set_active(bool active)
609     this->active = active;
610     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
611     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
612         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
613         if (ti) {
614             // *should* have one child that is the SPButton
615             Gtk::Widget* child = ti->get_child();
616             if ( child && SP_IS_BUTTON(child->gobj()) ) {
617                 SPButton* button = SP_BUTTON(child->gobj());
618                 sp_button_toggle_set_down( button, active );
619             }
620         }
621     }
624 void VerbAction::on_activate()
626     if ( verb ) {
627         SPAction *action = verb->get_action(view);
628         if ( action ) {
629             sp_action_perform(action, 0);
630         }
631     }
634 /* Global text entry widgets necessary for update */
635 /* GtkWidget *dropper_rgb_entry,
636           *dropper_opacity_entry ; */
637 // should be made a private member once this is converted to class
639 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
640     connection->disconnect();
641     delete connection;
644 static void purge_repr_listener( GObject* obj, GObject* tbl )
646     (void)obj;
647     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
648     if (oldrepr) { // remove old listener
649         sp_repr_remove_listener_by_data(oldrepr, tbl);
650         Inkscape::GC::release(oldrepr);
651         oldrepr = 0;
652         g_object_set_data( tbl, "repr", NULL );
653     }
656 GtkWidget *
657 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
658                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
659                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
661     SPAction *action = verb->get_action(view);
662     if (!action) return NULL;
664     SPAction *doubleclick_action;
665     if (doubleclick_verb)
666         doubleclick_action = doubleclick_verb->get_action(view);
667     else
668         doubleclick_action = NULL;
670     /* fixme: Handle sensitive/unsensitive */
671     /* fixme: Implement sp_button_new_from_action */
672     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
673     gtk_widget_show(b);
676     unsigned int shortcut = sp_shortcut_get_primary(verb);
677     if (shortcut) {
678         gchar key[256];
679         sp_ui_shortcut_string(shortcut, key);
680         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
681         if ( t ) {
682             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
683         }
684         g_free(tip);
685     } else {
686         if ( t ) {
687             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
688         }
689     }
691     return b;
695 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
697     SPAction* targetAction = SP_ACTION(user_data);
698     if ( targetAction ) {
699         sp_action_perform( targetAction, NULL );
700     }
703 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
705     if ( data ) {
706         GtkAction* act = GTK_ACTION(data);
707         gtk_action_set_sensitive( act, sensitive );
708     }
711 static SPActionEventVector action_event_vector = {
712     {NULL},
713     NULL,
714     NULL,
715     sp_action_action_set_sensitive,
716     NULL,
717     NULL
718 };
720 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
722     GtkAction* act = 0;
724     SPAction* targetAction = verb->get_action(view);
725     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
726     act = GTK_ACTION(inky);
727     gtk_action_set_sensitive( act, targetAction->sensitive );
729     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
731     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
732     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
734     return act;
737 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
739     Inkscape::UI::View::View *view = desktop;
740     gint verbsToUse[] = {
741         // disabled until we have icons for them:
742         //find
743         //SP_VERB_EDIT_TILE,
744         //SP_VERB_EDIT_UNTILE,
745         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
746         SP_VERB_DIALOG_SPRAY_OPTION,
747         SP_VERB_DIALOG_DISPLAY,
748         SP_VERB_DIALOG_FILL_STROKE,
749         SP_VERB_DIALOG_NAMEDVIEW,
750         SP_VERB_DIALOG_TEXT,
751         SP_VERB_DIALOG_XML_EDITOR,
752         SP_VERB_DIALOG_LAYERS,
753         SP_VERB_EDIT_CLONE,
754         SP_VERB_EDIT_COPY,
755         SP_VERB_EDIT_CUT,
756         SP_VERB_EDIT_DUPLICATE,
757         SP_VERB_EDIT_PASTE,
758         SP_VERB_EDIT_REDO,
759         SP_VERB_EDIT_UNDO,
760         SP_VERB_EDIT_UNLINK_CLONE,
761         SP_VERB_FILE_EXPORT,
762         SP_VERB_FILE_IMPORT,
763         SP_VERB_FILE_NEW,
764         SP_VERB_FILE_OPEN,
765         SP_VERB_FILE_PRINT,
766         SP_VERB_FILE_SAVE,
767         SP_VERB_OBJECT_TO_CURVE,
768         SP_VERB_SELECTION_GROUP,
769         SP_VERB_SELECTION_OUTLINE,
770         SP_VERB_SELECTION_UNGROUP,
771         SP_VERB_ZOOM_1_1,
772         SP_VERB_ZOOM_1_2,
773         SP_VERB_ZOOM_2_1,
774         SP_VERB_ZOOM_DRAWING,
775         SP_VERB_ZOOM_IN,
776         SP_VERB_ZOOM_NEXT,
777         SP_VERB_ZOOM_OUT,
778         SP_VERB_ZOOM_PAGE,
779         SP_VERB_ZOOM_PAGE_WIDTH,
780         SP_VERB_ZOOM_PREV,
781         SP_VERB_ZOOM_SELECTION,
782     };
784     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
786     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
787     Glib::RefPtr<Gtk::ActionGroup> mainActions;
788     if ( groups.find(desktop) != groups.end() ) {
789         mainActions = groups[desktop];
790     }
792     if ( !mainActions ) {
793         mainActions = Gtk::ActionGroup::create("main");
794         groups[desktop] = mainActions;
795     }
797     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
798         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
799         if ( verb ) {
800             if (!mainActions->get_action(verb->get_id())) {
801                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
802                 mainActions->add(Glib::wrap(act));
803             }
804         }
805     }
807     if ( !mainActions->get_action("ToolZoom") ) {
808         GtkTooltips *tt = gtk_tooltips_new();
809         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
810             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
811             if ( va ) {
812                 mainActions->add(va);
813                 if ( i == 0 ) {
814                     va->set_active(true);
815                 }
816             }
817         }
818     }
821     return mainActions;
825 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
827     gtk_widget_set_size_request( widget,
828                                  widget->allocation.width,
829                                  widget->allocation.height );
832 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
834     gtk_widget_set_size_request( widget, -1, -1 );
839 GtkWidget *
840 sp_tool_toolbox_new()
842     GtkTooltips *tt = gtk_tooltips_new();
843     GtkWidget* tb = gtk_toolbar_new();
844     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
845     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
847     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
848     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
850     gtk_widget_set_sensitive(tb, FALSE);
852     GtkWidget *hb = gtk_handle_box_new();
853     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
854     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
855     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
857     gtk_container_add(GTK_CONTAINER(hb), tb);
858     gtk_widget_show(GTK_WIDGET(tb));
860     sigc::connection* conn = new sigc::connection;
861     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
863     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
864     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
866     return hb;
869 GtkWidget *
870 sp_aux_toolbox_new()
872     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
874     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
876     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
878     gtk_widget_set_sensitive(tb, FALSE);
880     GtkWidget *hb = gtk_handle_box_new();
881     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
882     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
883     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
885     gtk_container_add(GTK_CONTAINER(hb), tb);
886     gtk_widget_show(GTK_WIDGET(tb));
888     sigc::connection* conn = new sigc::connection;
889     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
891     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
892     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
894     return hb;
897 //####################################
898 //# Commands Bar
899 //####################################
901 GtkWidget *
902 sp_commands_toolbox_new()
904     GtkWidget *tb = gtk_toolbar_new();
906     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
907     gtk_widget_set_sensitive(tb, FALSE);
909     GtkWidget *hb = gtk_handle_box_new();
910     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
911     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
912     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
914     gtk_container_add(GTK_CONTAINER(hb), tb);
915     gtk_widget_show(GTK_WIDGET(tb));
917     sigc::connection* conn = new sigc::connection;
918     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
920     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
921     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
923     return hb;
926 GtkWidget *
927 sp_snap_toolbox_new()
929         GtkWidget *tb = gtk_vbox_new(FALSE, 0);
930         gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
931         g_object_set_data(G_OBJECT(tb), "desktop", NULL);
933         //GtkWidget *tb = gtk_toolbar_new();
934     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
936     gtk_widget_set_sensitive(tb, FALSE);
938     GtkWidget *hb = gtk_handle_box_new();
939     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
940     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
941     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
943     gtk_container_add(GTK_CONTAINER(hb), tb);
944     gtk_widget_show(GTK_WIDGET(tb));
946     sigc::connection* conn = new sigc::connection;
947     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
949     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
950     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
952     return hb;
955 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
956                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
957                                                        Glib::ustring const &path, gdouble def,
958                                                        GtkWidget *focusTarget,
959                                                        GtkWidget *us,
960                                                        GObject *dataKludge,
961                                                        gboolean altx, gchar const *altx_mark,
962                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
963                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
964                                                        void (*callback)(GtkAdjustment *, GObject *),
965                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
967     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
968     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
969                                                              lower, upper, step, page, 0 ) );
970     if (us) {
971         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
972     }
974     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
976     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
977     if ( shortLabel ) {
978         g_object_set( act, "short_label", shortLabel, NULL );
979     }
981     if ( (descrCount > 0) && descrLabels && descrValues ) {
982         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
983     }
985     if ( focusTarget ) {
986         ege_adjustment_action_set_focuswidget( act, focusTarget );
987     }
989     if ( altx && altx_mark ) {
990         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
991     }
993     if ( dataKludge ) {
994         // Rather lame, but it's the only place where we need to get the entry name
995         // but we don't have an Entry
996         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
997     }
999     // Using a cast just to make sure we pass in the right kind of function pointer
1000     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1002     return act;
1006 //####################################
1007 //# node editing callbacks
1008 //####################################
1010 /**
1011  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1012  */
1013 static ShapeEditor *get_current_shape_editor()
1015     if (!SP_ACTIVE_DESKTOP) {
1016         return NULL;
1017     }
1019     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1021     if (!SP_IS_NODE_CONTEXT(event_context)) {
1022         return NULL;
1023     }
1025     return event_context->shape_editor;
1029 void
1030 sp_node_path_edit_add(void)
1032     ShapeEditor *shape_editor = get_current_shape_editor();
1033     if (shape_editor) shape_editor->add_node();
1036 void
1037 sp_node_path_edit_delete(void)
1039     ShapeEditor *shape_editor = get_current_shape_editor();
1040     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1043 void
1044 sp_node_path_edit_delete_segment(void)
1046     ShapeEditor *shape_editor = get_current_shape_editor();
1047     if (shape_editor) shape_editor->delete_segment();
1050 void
1051 sp_node_path_edit_break(void)
1053     ShapeEditor *shape_editor = get_current_shape_editor();
1054     if (shape_editor) shape_editor->break_at_nodes();
1057 void
1058 sp_node_path_edit_join(void)
1060     ShapeEditor *shape_editor = get_current_shape_editor();
1061     if (shape_editor) shape_editor->join_nodes();
1064 void
1065 sp_node_path_edit_join_segment(void)
1067     ShapeEditor *shape_editor = get_current_shape_editor();
1068     if (shape_editor) shape_editor->join_segments();
1071 void
1072 sp_node_path_edit_toline(void)
1074     ShapeEditor *shape_editor = get_current_shape_editor();
1075     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1078 void
1079 sp_node_path_edit_tocurve(void)
1081     ShapeEditor *shape_editor = get_current_shape_editor();
1082     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1085 void
1086 sp_node_path_edit_cusp(void)
1088     ShapeEditor *shape_editor = get_current_shape_editor();
1089     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1092 void
1093 sp_node_path_edit_smooth(void)
1095     ShapeEditor *shape_editor = get_current_shape_editor();
1096     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1099 void
1100 sp_node_path_edit_symmetrical(void)
1102     ShapeEditor *shape_editor = get_current_shape_editor();
1103     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1106 void
1107 sp_node_path_edit_auto(void)
1109     ShapeEditor *shape_editor = get_current_shape_editor();
1110     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1113 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1114     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1115     bool show = gtk_toggle_action_get_active( act );
1116     prefs->setBool("/tools/nodes/show_handles",  show);
1117     ShapeEditor *shape_editor = get_current_shape_editor();
1118     if (shape_editor) shape_editor->show_handles(show);
1121 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1122     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1123     bool show = gtk_toggle_action_get_active( act );
1124     prefs->setBool("/tools/nodes/show_helperpath",  show);
1125     ShapeEditor *shape_editor = get_current_shape_editor();
1126     if (shape_editor) shape_editor->show_helperpath(show);
1129 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1130     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1133 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1134     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1137 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1138     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1141 /* is called when the node selection is modified */
1142 static void
1143 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1145     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1146     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1147     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1148     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1150     // quit if run by the attr_changed listener
1151     if (g_object_get_data( tbl, "freeze" )) {
1152         return;
1153     }
1155     // in turn, prevent listener from responding
1156     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1158     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1159     SPUnit const *unit = tracker->getActiveUnit();
1161     ShapeEditor *shape_editor = get_current_shape_editor();
1162     if (shape_editor && shape_editor->has_nodepath()) {
1163         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1164         int n_selected = 0;
1165         if (nodepath) {
1166             n_selected = nodepath->numSelected();
1167         }
1169         if (n_selected == 0) {
1170             gtk_action_set_sensitive(xact, FALSE);
1171             gtk_action_set_sensitive(yact, FALSE);
1172         } else {
1173             gtk_action_set_sensitive(xact, TRUE);
1174             gtk_action_set_sensitive(yact, TRUE);
1175             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1176             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1178             if (n_selected == 1) {
1179                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1180                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1181                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1182                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1183                 }
1184             } else {
1185                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1186                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1187                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1188                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1189                        selected nodes don't coincide (in this case we use the coordinates of the center
1190                        of the bounding box). So the entries are never set to zero. */
1191                     // FIXME: Maybe we should clear the entry if several nodes are selected
1192                     //        instead of providing a kind of average value
1193                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1194                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1195                 }
1196             }
1197         }
1198     } else {
1199         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1200         gtk_action_set_sensitive(xact, FALSE);
1201         gtk_action_set_sensitive(yact, FALSE);
1202     }
1204     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1207 static void
1208 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1210     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1211     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1213     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1214     SPUnit const *unit = tracker->getActiveUnit();
1216     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1217         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1218     }
1220     // quit if run by the attr_changed listener
1221     if (g_object_get_data( tbl, "freeze" )) {
1222         return;
1223     }
1225     // in turn, prevent listener from responding
1226     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1228     ShapeEditor *shape_editor = get_current_shape_editor();
1229     if (shape_editor && shape_editor->has_nodepath()) {
1230         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1231         if (!strcmp(value_name, "x")) {
1232             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1233         }
1234         if (!strcmp(value_name, "y")) {
1235             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1236         }
1237     }
1239     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1242 static void
1243 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1245     sp_node_path_value_changed(adj, tbl, "x");
1248 static void
1249 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1251     sp_node_path_value_changed(adj, tbl, "y");
1254 void
1255 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1257     {
1258     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1259     SPItem *item = selection->singleItem();
1260     if (item && SP_IS_LPE_ITEM(item)) {
1261        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1262            gtk_action_set_sensitive(w, TRUE);
1263        } else {
1264            gtk_action_set_sensitive(w, FALSE);
1265        }
1266     } else {
1267        gtk_action_set_sensitive(w, FALSE);
1268     }
1269     }
1271     {
1272     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1273     SPItem *item = selection->singleItem();
1274     if (item && item->clip_ref && item->clip_ref->getObject()) {
1275        gtk_action_set_sensitive(w, TRUE);
1276     } else {
1277        gtk_action_set_sensitive(w, FALSE);
1278     }
1279     }
1281     {
1282     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1283     SPItem *item = selection->singleItem();
1284     if (item && item->mask_ref && item->mask_ref->getObject()) {
1285        gtk_action_set_sensitive(w, TRUE);
1286     } else {
1287        gtk_action_set_sensitive(w, FALSE);
1288     }
1289     }
1292 void
1293 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1295     sp_node_toolbox_sel_changed (selection, tbl);
1300 //################################
1301 //##    Node Editing Toolbox    ##
1302 //################################
1304 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1306     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1307     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1308     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1309     g_object_set_data( holder, "tracker", tracker );
1311     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1313     {
1314         InkAction* inky = ink_action_new( "NodeInsertAction",
1315                                           _("Insert node"),
1316                                           _("Insert new nodes into selected segments"),
1317                                           INKSCAPE_ICON_NODE_ADD,
1318                                           secondarySize );
1319         g_object_set( inky, "short_label", _("Insert"), NULL );
1320         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1321         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1322     }
1324     {
1325         InkAction* inky = ink_action_new( "NodeDeleteAction",
1326                                           _("Delete node"),
1327                                           _("Delete selected nodes"),
1328                                           INKSCAPE_ICON_NODE_DELETE,
1329                                           secondarySize );
1330         g_object_set( inky, "short_label", _("Delete"), NULL );
1331         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1332         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1333     }
1335     {
1336         InkAction* inky = ink_action_new( "NodeJoinAction",
1337                                           _("Join endnodes"),
1338                                           _("Join selected endnodes"),
1339                                           INKSCAPE_ICON_NODE_JOIN,
1340                                           secondarySize );
1341         g_object_set( inky, "short_label", _("Join"), NULL );
1342         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1343         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1344     }
1346     {
1347         InkAction* inky = ink_action_new( "NodeBreakAction",
1348                                           _("Break nodes"),
1349                                           _("Break path at selected nodes"),
1350                                           INKSCAPE_ICON_NODE_BREAK,
1351                                           secondarySize );
1352         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1353         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1354     }
1357     {
1358         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1359                                           _("Join with segment"),
1360                                           _("Join selected endnodes with a new segment"),
1361                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1362                                           secondarySize );
1363         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1364         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365     }
1367     {
1368         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1369                                           _("Delete segment"),
1370                                           _("Delete segment between two non-endpoint nodes"),
1371                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1372                                           secondarySize );
1373         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1374         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375     }
1377     {
1378         InkAction* inky = ink_action_new( "NodeCuspAction",
1379                                           _("Node Cusp"),
1380                                           _("Make selected nodes corner"),
1381                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1382                                           secondarySize );
1383         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1384         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1385     }
1387     {
1388         InkAction* inky = ink_action_new( "NodeSmoothAction",
1389                                           _("Node Smooth"),
1390                                           _("Make selected nodes smooth"),
1391                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1392                                           secondarySize );
1393         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1394         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1395     }
1397     {
1398         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1399                                           _("Node Symmetric"),
1400                                           _("Make selected nodes symmetric"),
1401                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1402                                           secondarySize );
1403         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1404         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1405     }
1407     {
1408         InkAction* inky = ink_action_new( "NodeAutoAction",
1409                                           _("Node Auto"),
1410                                           _("Make selected nodes auto-smooth"),
1411                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1412                                           secondarySize );
1413         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1414         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1415     }
1417     {
1418         InkAction* inky = ink_action_new( "NodeLineAction",
1419                                           _("Node Line"),
1420                                           _("Make selected segments lines"),
1421                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1422                                           secondarySize );
1423         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1424         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1425     }
1427     {
1428         InkAction* inky = ink_action_new( "NodeCurveAction",
1429                                           _("Node Curve"),
1430                                           _("Make selected segments curves"),
1431                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1432                                           secondarySize );
1433         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1434         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1435     }
1437     {
1438         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1439                                                       _("Show Handles"),
1440                                                       _("Show the Bezier handles of selected nodes"),
1441                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1442                                                       Inkscape::ICON_SIZE_DECORATION );
1443         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1444         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1445         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1446     }
1448     {
1449         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1450                                                       _("Show Outline"),
1451                                                       _("Show the outline of the path"),
1452                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1453                                                       Inkscape::ICON_SIZE_DECORATION );
1454         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1455         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1456         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1457     }
1459     {
1460         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1461                                           _("Next path effect parameter"),
1462                                           _("Show next path effect parameter for editing"),
1463                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1464                                           Inkscape::ICON_SIZE_DECORATION );
1465         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1466         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1467         g_object_set_data( holder, "nodes_lpeedit", inky);
1468     }
1470     {
1471         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1472                                           _("Edit clipping path"),
1473                                           _("Edit the clipping path of the object"),
1474                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1475                                           Inkscape::ICON_SIZE_DECORATION );
1476         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1477         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1478         g_object_set_data( holder, "nodes_clippathedit", inky);
1479     }
1481     {
1482         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1483                                           _("Edit mask path"),
1484                                           _("Edit the mask of the object"),
1485                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1486                                           Inkscape::ICON_SIZE_DECORATION );
1487         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1488         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1489         g_object_set_data( holder, "nodes_maskedit", inky);
1490     }
1492     /* X coord of selected node(s) */
1493     {
1494         EgeAdjustmentAction* eact = 0;
1495         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1496         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1497         eact = create_adjustment_action( "NodeXAction",
1498                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1499                                          "/tools/nodes/Xcoord", 0,
1500                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1501                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1502                                          labels, values, G_N_ELEMENTS(labels),
1503                                          sp_node_path_x_value_changed );
1504         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1505         g_object_set_data( holder, "nodes_x_action", eact );
1506         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1507         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1508     }
1510     /* Y coord of selected node(s) */
1511     {
1512         EgeAdjustmentAction* eact = 0;
1513         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1514         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1515         eact = create_adjustment_action( "NodeYAction",
1516                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1517                                          "/tools/nodes/Ycoord", 0,
1518                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1519                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1520                                          labels, values, G_N_ELEMENTS(labels),
1521                                          sp_node_path_y_value_changed );
1522         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1523         g_object_set_data( holder, "nodes_y_action", eact );
1524         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1525         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1526     }
1528     // add the units menu
1529     {
1530         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1531         gtk_action_group_add_action( mainActions, act );
1532     }
1535     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1537     //watch selection
1538     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1540     sigc::connection *c_selection_changed =
1541         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1542                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1543     pool->add_connection ("selection-changed", c_selection_changed);
1545     sigc::connection *c_selection_modified =
1546         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1547                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1548     pool->add_connection ("selection-modified", c_selection_modified);
1550     sigc::connection *c_subselection_changed =
1551         new sigc::connection (desktop->connectToolSubselectionChanged
1552                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1553     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1555     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1557     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1558 } // end of sp_node_toolbox_prep()
1561 //########################
1562 //##    Zoom Toolbox    ##
1563 //########################
1565 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1567     // no custom GtkAction setup needed
1568 } // end of sp_zoom_toolbox_prep()
1570 void
1571 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1573     toolbox_set_desktop(toolbox,
1574                         desktop,
1575                         setup_tool_toolbox,
1576                         update_tool_toolbox,
1577                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1578                                                                          "event_context_connection")));
1582 void
1583 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1585     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1586                         desktop,
1587                         setup_aux_toolbox,
1588                         update_aux_toolbox,
1589                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1590                                                                          "event_context_connection")));
1593 void
1594 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1596     toolbox_set_desktop(toolbox,
1597                         desktop,
1598                         setup_commands_toolbox,
1599                         update_commands_toolbox,
1600                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1601                                                                          "event_context_connection")));
1604 void
1605 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1607     toolbox_set_desktop(toolbox,
1608                         desktop,
1609                         setup_snap_toolbox,
1610                         update_snap_toolbox,
1611                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1612                                                                          "event_context_connection")));
1616 static void
1617 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1619     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1620     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1622     if (old_desktop) {
1623         GList *children, *iter;
1625         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1626         for ( iter = children ; iter ; iter = iter->next ) {
1627             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1628         }
1629         g_list_free(children);
1630     }
1632     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1634     if (desktop) {
1635         gtk_widget_set_sensitive(toolbox, TRUE);
1636         setup_func(toolbox, desktop);
1637         update_func(desktop, desktop->event_context, toolbox);
1638         *conn = desktop->connectEventContextChanged
1639             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1640     } else {
1641         gtk_widget_set_sensitive(toolbox, FALSE);
1642     }
1644 } // end of toolbox_set_desktop()
1647 static void
1648 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1650     gchar const * descr =
1651         "<ui>"
1652         "  <toolbar name='ToolToolbar'>"
1653         "    <toolitem action='ToolSelector' />"
1654         "    <toolitem action='ToolNode' />"
1655         "    <toolitem action='ToolTweak' />"
1656         "    <toolitem action='ToolSpray' />"
1657         "    <toolitem action='ToolZoom' />"
1658         "    <toolitem action='ToolRect' />"
1659         "    <toolitem action='Tool3DBox' />"
1660         "    <toolitem action='ToolArc' />"
1661         "    <toolitem action='ToolStar' />"
1662         "    <toolitem action='ToolSpiral' />"
1663         "    <toolitem action='ToolPencil' />"
1664         "    <toolitem action='ToolPen' />"
1665         "    <toolitem action='ToolCalligraphic' />"
1666         "    <toolitem action='ToolEraser' />"
1667 //        "    <toolitem action='ToolLPETool' />"
1668         "    <toolitem action='ToolPaintBucket' />"
1669         "    <toolitem action='ToolText' />"
1670         "    <toolitem action='ToolConnector' />"
1671         "    <toolitem action='ToolGradient' />"
1672         "    <toolitem action='ToolDropper' />"
1673         "  </toolbar>"
1674         "</ui>";
1675     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1676     GtkUIManager* mgr = gtk_ui_manager_new();
1677     GError* errVal = 0;
1678     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1680     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1681     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1683     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1684     if ( prefs->getBool("/toolbox/icononly", true) ) {
1685         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1686     }
1687     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1688     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1690     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1691     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1693     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1695     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1696     if ( child ) {
1697         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1698     }
1700     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1701 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1705 static void
1706 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1708     gchar const *const tname = ( eventcontext
1709                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1710                                  : NULL );
1711     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1713     for (int i = 0 ; tools[i].type_name ; i++ ) {
1714         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1715         if ( act ) {
1716             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1717             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1718             if ( verbAct ) {
1719                 verbAct->set_active(setActive);
1720             }
1721         }
1722     }
1725 static void
1726 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1728     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1729     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1730     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1731     GtkUIManager* mgr = gtk_ui_manager_new();
1732     GError* errVal = 0;
1733     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1734     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1736     std::map<std::string, GtkWidget*> dataHolders;
1738     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1739         if ( aux_toolboxes[i].prep_func ) {
1740             // converted to GtkActions and UIManager
1742             GtkWidget* kludge = gtk_toolbar_new();
1743             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1744             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1745             dataHolders[aux_toolboxes[i].type_name] = kludge;
1746             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1747         } else {
1749             GtkWidget *sub_toolbox = 0;
1750             if (aux_toolboxes[i].create_func == NULL)
1751                 sub_toolbox = sp_empty_toolbox_new(desktop);
1752             else {
1753                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1754             }
1756             gtk_size_group_add_widget( grouper, sub_toolbox );
1758             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1759             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1761         }
1762     }
1764     // Second pass to create toolbars *after* all GtkActions are created
1765     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1766         if ( aux_toolboxes[i].prep_func ) {
1767             // converted to GtkActions and UIManager
1769             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1771             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1772             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1774             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1775             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1776             g_free( tmp );
1777             tmp = 0;
1779             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1780                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1781             }
1783             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1784             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1786             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1788             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1789                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1790                 swatch->setDesktop( desktop );
1791                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1792                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1793                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1794                 gtk_table_attach( GTK_TABLE(holder), swatch_, 1, 2, 0, 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), AUX_BETWEEN_BUTTON_GROUPS, 0 );
1795             }
1797             gtk_widget_show_all( holder );
1798             sp_set_font_size_smaller( holder );
1800             gtk_size_group_add_widget( grouper, holder );
1802             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1803             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1804         }
1805     }
1807     g_object_unref( G_OBJECT(grouper) );
1810 static void
1811 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1813     gchar const *tname = ( eventcontext
1814                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1815                            : NULL );
1816     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1817         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1818         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1819             gtk_widget_show_all(sub_toolbox);
1820             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1821         } else {
1822             gtk_widget_hide(sub_toolbox);
1823         }
1824     }
1827 static void
1828 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1830     gchar const * descr =
1831         "<ui>"
1832         "  <toolbar name='CommandsToolbar'>"
1833         "    <toolitem action='FileNew' />"
1834         "    <toolitem action='FileOpen' />"
1835         "    <toolitem action='FileSave' />"
1836         "    <toolitem action='FilePrint' />"
1837         "    <separator />"
1838         "    <toolitem action='FileImport' />"
1839         "    <toolitem action='FileExport' />"
1840         "    <separator />"
1841         "    <toolitem action='EditUndo' />"
1842         "    <toolitem action='EditRedo' />"
1843         "    <separator />"
1844         "    <toolitem action='EditCopy' />"
1845         "    <toolitem action='EditCut' />"
1846         "    <toolitem action='EditPaste' />"
1847         "    <separator />"
1848         "    <toolitem action='ZoomSelection' />"
1849         "    <toolitem action='ZoomDrawing' />"
1850         "    <toolitem action='ZoomPage' />"
1851         "    <separator />"
1852         "    <toolitem action='EditDuplicate' />"
1853         "    <toolitem action='EditClone' />"
1854         "    <toolitem action='EditUnlinkClone' />"
1855         "    <separator />"
1856         "    <toolitem action='SelectionGroup' />"
1857         "    <toolitem action='SelectionUnGroup' />"
1858         "    <separator />"
1859         "    <toolitem action='DialogFillStroke' />"
1860         "    <toolitem action='DialogText' />"
1861         "    <toolitem action='DialogLayers' />"
1862         "    <toolitem action='DialogXMLEditor' />"
1863         "    <toolitem action='DialogAlignDistribute' />"
1864         "    <separator />"
1865         "    <toolitem action='DialogPreferences' />"
1866         "    <toolitem action='DialogDocumentProperties' />"
1867         "  </toolbar>"
1868         "</ui>";
1869     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1870     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1872     GtkUIManager* mgr = gtk_ui_manager_new();
1873     GError* errVal = 0;
1875     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1876     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1878     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1879     if ( prefs->getBool("/toolbox/icononly", true) ) {
1880         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1881     }
1883     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1884     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1886     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1887     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1890     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1892     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1893     if ( child ) {
1894         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1895     }
1897     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1900 static void
1901 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1905 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1907         if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1908                 return;
1909         }
1911         gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1912         g_assert(ptr != NULL);
1914         SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1915         SPNamedView *nv = sp_desktop_namedview(dt);
1916         SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1918         if (dt == NULL || nv == NULL) {
1919                 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1920                 return;
1921         }
1923         Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1925         if (repr == NULL) {
1926                 g_warning("This namedview doesn't have a xml representation attached!");
1927                 return;
1928         }
1930         bool saved = sp_document_get_undo_sensitive(doc);
1931         sp_document_set_undo_sensitive(doc, false);
1933         bool v = false;
1934         SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1936         switch (attr) {
1937         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1938                 dt->toggleSnapGlobal();
1939                 break;
1940         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1941                 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1942                 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1943                 break;
1944         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1945                 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1946                 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1947                 break;
1948         case SP_ATTR_INKSCAPE_BBOX_NODES:
1949                 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1950                 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1951                 break;
1952         case SP_ATTR_INKSCAPE_SNAP_NODES:
1953                 v = nv->snap_manager.snapprefs.getSnapModeNode();
1954                 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1955                 break;
1956         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1957                 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1958                 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1959                 break;
1960         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1961                 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1962                 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1963                 break;
1964         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1965                 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1966                 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1967                 break;
1968         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1969                 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1970                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1971                 break;
1972         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1973                 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1974                 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1975                 break;
1976         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1977                 v = nv->snap_manager.snapprefs.getSnapToGrids();
1978                 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1979                 break;
1980         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1981                 v = nv->snap_manager.snapprefs.getSnapToGuides();
1982                 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1983                 break;
1984         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1985                 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1986                 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1987                 break;
1988         /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1989                 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1990                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1991                 break;*/
1992         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1993                 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1994                 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1995                 break;
1996         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1997                 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1998                 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1999                 break;
2000         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2001                 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2002                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2003                 break;
2004         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2005                 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2006                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2007                 break;
2008         default:
2009                 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2010                 break;
2011         }
2013         // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2014         doc->setModifiedSinceSave();
2016         sp_document_set_undo_sensitive(doc, saved);
2019 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2021         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2022         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2024         gchar const * descr =
2025                 "<ui>"
2026                 "  <toolbar name='SnapToolbar'>"
2027                 "    <toolitem action='ToggleSnapGlobal' />"
2028                         "    <separator />"
2029                         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2030                         "    <toolitem action='ToggleSnapToBBoxPath' />"
2031                         "    <toolitem action='ToggleSnapToBBoxNode' />"
2032                         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2033                         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2034                         "    <separator />"
2035                         "    <toolitem action='ToggleSnapFromNode' />"
2036                         "    <toolitem action='ToggleSnapToItemPath' />"
2037                         "    <toolitem action='ToggleSnapToPathIntersections' />"
2038                         "    <toolitem action='ToggleSnapToItemNode' />"
2039                         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2040                         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2041                         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2042                         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2043                         "    <separator />"
2044                         "    <toolitem action='ToggleSnapToPageBorder' />"
2045                         "    <toolitem action='ToggleSnapToGrids' />"
2046                         "    <toolitem action='ToggleSnapToGuides' />"
2047                   //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2048                         "  </toolbar>"
2049                 "</ui>";
2051         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2053         {
2054                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2055                     _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2056                     SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2058                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2059                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2060         }
2062         {
2063                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2064                     _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2065                     secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2067                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2068                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2069         }
2071         {
2072                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2073                     _("Bounding box edges"), _("Snap to edges of a bounding box"),
2074                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2076                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2077                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2078         }
2080         {
2081                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2082                     _("Bounding box corners"), _("Snap to bounding box corners"),
2083                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2085                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2086                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2087         }
2089         {
2090                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2091                     _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2092                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2093                     SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2095                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2096                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2097         }
2099         {
2100                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2101                     _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2102                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2104                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2105                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2106         }
2108         {
2109                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2110                     _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2112                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2113                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2114         }
2116         {
2117                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2118                     _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2119                     SP_ATTR_INKSCAPE_OBJECT_PATHS);
2121                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2122                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2123         }
2125         {
2126                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2127                     _("Path intersections"), _("Snap to path intersections"),
2128                     INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2130                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2131                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2132         }
2134         {
2135                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2136                     _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2137                     SP_ATTR_INKSCAPE_OBJECT_NODES);
2139                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2140                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2141         }
2143         {
2144                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2145                     _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2146                     secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2148                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2149                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2150         }
2152         {
2153                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2154                     _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2155                     INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2157                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2158                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2159         }
2161         {
2162                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2163                     _("Object Centers"), _("Snap from and to centers of objects"),
2164                     INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2166                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2167                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2168         }
2170         {
2171                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2172                     _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2173                     INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2175                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2176                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2177         }
2179         {
2180                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2181                     _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2182                     secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2184                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2185                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2186         }
2188         {
2189                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2190                     _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2191                     SP_ATTR_INKSCAPE_SNAP_GRIDS);
2193                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2194                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2195         }
2197         {
2198                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2199                     _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2200                     SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2202                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2203                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2204         }
2206         /*{
2207                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2208                     _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2209                     INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2210                     SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2212                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2213                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2214         }*/
2216     GtkUIManager* mgr = gtk_ui_manager_new();
2217     GError* errVal = 0;
2219     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2220     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2222     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2223     if ( prefs->getBool("/toolbox/icononly", true) ) {
2224         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2225     }
2227     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2228     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2230     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2231     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2233     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2235     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2236     if ( child ) {
2237         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2238     }
2240     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2244 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2246         g_assert(desktop != NULL);
2247         g_assert(toolbox != NULL);
2249         SPNamedView *nv = sp_desktop_namedview(desktop);
2250         if (nv == NULL) {
2251                 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2252                 return;
2253         }
2255         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2257         Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2258         Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2259         Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2260         Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2261         Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2262         Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2263         Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2264         Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2265         Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2266         Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2267         Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2268         Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2269         Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2270         Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2271         Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2272         //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2273         Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2274         Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2277         if (!act1) {
2278                 return; // The snap actions haven't been defined yet (might be the case during startup)
2279         }
2281         // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2282         // changes in our document because we're only updating the UI;
2283         // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2284         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2286         bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2287         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2289         bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2290         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2291         gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2293         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2294         gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2295         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2296         gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2297         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2298         gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2299         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2300         gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2302         bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2303         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2304         gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2306         bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2307         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2308         gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2309         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2310         gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2311         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2312         gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2313         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2314         gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2315         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2316         gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2317         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2318         gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2319         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2320         gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2322         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2323         gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2324         //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2325         //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2327         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2328         gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2329         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2330         gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2333         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2336 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2338     gtk_widget_show(toolbox_toplevel);
2339     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2341     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2342     if (!shown_toolbox) {
2343         return;
2344     }
2345     gtk_widget_show(toolbox);
2347     gtk_widget_show_all(shown_toolbox);
2350 static GtkWidget *
2351 sp_empty_toolbox_new(SPDesktop *desktop)
2353     GtkWidget *tbl = gtk_toolbar_new();
2354     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2355     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2357     gtk_widget_show_all(tbl);
2358     sp_set_font_size_smaller (tbl);
2360     return tbl;
2363 #define MODE_LABEL_WIDTH 70
2365 //########################
2366 //##       Star         ##
2367 //########################
2369 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2371     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2373     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2374         // do not remember prefs if this call is initiated by an undo change, because undoing object
2375         // creation sets bogus values to its attributes before it is deleted
2376         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2377         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2378     }
2380     // quit if run by the attr_changed listener
2381     if (g_object_get_data( dataKludge, "freeze" )) {
2382         return;
2383     }
2385     // in turn, prevent listener from responding
2386     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2388     bool modmade = false;
2390     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2391     GSList const *items = selection->itemList();
2392     for (; items != NULL; items = items->next) {
2393         if (SP_IS_STAR((SPItem *) items->data)) {
2394             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2395             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2396             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2397                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2398                                     + M_PI / (gint)adj->value));
2399             SP_OBJECT((SPItem *) items->data)->updateRepr();
2400             modmade = true;
2401         }
2402     }
2403     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2404                                    _("Star: Change number of corners"));
2406     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2409 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2411     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2413     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2414         if (!IS_NAN(adj->value)) {
2415                         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2416                         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2417         }
2418     }
2420     // quit if run by the attr_changed listener
2421     if (g_object_get_data( dataKludge, "freeze" )) {
2422         return;
2423     }
2425     // in turn, prevent listener from responding
2426     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2428     bool modmade = false;
2429     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2430     GSList const *items = selection->itemList();
2431     for (; items != NULL; items = items->next) {
2432         if (SP_IS_STAR((SPItem *) items->data)) {
2433             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2435             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2436             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2437             if (r2 < r1) {
2438                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2439             } else {
2440                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2441             }
2443             SP_OBJECT((SPItem *) items->data)->updateRepr();
2444             modmade = true;
2445         }
2446     }
2448     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2449                                    _("Star: Change spoke ratio"));
2451     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2454 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2456     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2457     bool flat = ege_select_one_action_get_active( act ) == 0;
2459     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2460         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2461         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2462     }
2464     // quit if run by the attr_changed listener
2465     if (g_object_get_data( dataKludge, "freeze" )) {
2466         return;
2467     }
2469     // in turn, prevent listener from responding
2470     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2472     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2473     GSList const *items = selection->itemList();
2474     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2475     bool modmade = false;
2477     if ( prop_action ) {
2478         gtk_action_set_sensitive( prop_action, !flat );
2479     }
2481     for (; items != NULL; items = items->next) {
2482         if (SP_IS_STAR((SPItem *) items->data)) {
2483             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2484             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2485             SP_OBJECT((SPItem *) items->data)->updateRepr();
2486             modmade = true;
2487         }
2488     }
2490     if (modmade) {
2491         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2492                          flat ? _("Make polygon") : _("Make star"));
2493     }
2495     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2498 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2500     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2502     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2503         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2504         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2505     }
2507     // quit if run by the attr_changed listener
2508     if (g_object_get_data( dataKludge, "freeze" )) {
2509         return;
2510     }
2512     // in turn, prevent listener from responding
2513     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2515     bool modmade = false;
2517     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2518     GSList const *items = selection->itemList();
2519     for (; items != NULL; items = items->next) {
2520         if (SP_IS_STAR((SPItem *) items->data)) {
2521             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2522             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2523             SP_OBJECT(items->data)->updateRepr();
2524             modmade = true;
2525         }
2526     }
2527     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2528                                    _("Star: Change rounding"));
2530     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2533 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2535     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2537     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2538         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2539         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2540     }
2542     // quit if run by the attr_changed listener
2543     if (g_object_get_data( dataKludge, "freeze" )) {
2544         return;
2545     }
2547     // in turn, prevent listener from responding
2548     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2550     bool modmade = false;
2552     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2553     GSList const *items = selection->itemList();
2554     for (; items != NULL; items = items->next) {
2555         if (SP_IS_STAR((SPItem *) items->data)) {
2556             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2557             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2558             SP_OBJECT(items->data)->updateRepr();
2559             modmade = true;
2560         }
2561     }
2562     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2563                                    _("Star: Change randomization"));
2565     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2569 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2570                                        gchar const */*old_value*/, gchar const */*new_value*/,
2571                                        bool /*is_interactive*/, gpointer data)
2573     GtkWidget *tbl = GTK_WIDGET(data);
2575     // quit if run by the _changed callbacks
2576     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2577         return;
2578     }
2580     // in turn, prevent callbacks from responding
2581     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2583     GtkAdjustment *adj = 0;
2585     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2586     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2588     if (!strcmp(name, "inkscape:randomized")) {
2589         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2590         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2591     } else if (!strcmp(name, "inkscape:rounded")) {
2592         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2593         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2594     } else if (!strcmp(name, "inkscape:flatsided")) {
2595         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2596         char const *flatsides = repr->attribute("inkscape:flatsided");
2597         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2598         if ( flatsides && !strcmp(flatsides,"false") ) {
2599             ege_select_one_action_set_active( flat_action, 1 );
2600             gtk_action_set_sensitive( prop_action, TRUE );
2601         } else {
2602             ege_select_one_action_set_active( flat_action, 0 );
2603             gtk_action_set_sensitive( prop_action, FALSE );
2604         }
2605     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2606         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2607         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2608         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2609         if (r2 < r1) {
2610             gtk_adjustment_set_value(adj, r2/r1);
2611         } else {
2612             gtk_adjustment_set_value(adj, r1/r2);
2613         }
2614     } else if (!strcmp(name, "sodipodi:sides")) {
2615         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2616         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2617     }
2619     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2623 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2625     NULL, /* child_added */
2626     NULL, /* child_removed */
2627     star_tb_event_attr_changed,
2628     NULL, /* content_changed */
2629     NULL  /* order_changed */
2630 };
2633 /**
2634  *  \param selection Should not be NULL.
2635  */
2636 static void
2637 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2639     int n_selected = 0;
2640     Inkscape::XML::Node *repr = NULL;
2642     purge_repr_listener( tbl, tbl );
2644     for (GSList const *items = selection->itemList();
2645          items != NULL;
2646          items = items->next)
2647     {
2648         if (SP_IS_STAR((SPItem *) items->data)) {
2649             n_selected++;
2650             repr = SP_OBJECT_REPR((SPItem *) items->data);
2651         }
2652     }
2654     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2656     if (n_selected == 0) {
2657         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2658     } else if (n_selected == 1) {
2659         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2661         if (repr) {
2662             g_object_set_data( tbl, "repr", repr );
2663             Inkscape::GC::anchor(repr);
2664             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2665             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2666         }
2667     } else {
2668         // FIXME: implement averaging of all parameters for multiple selected stars
2669         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2670         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2671     }
2675 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2677     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2678     // callbacks to lump all the changes for all selected objects in one undo step
2680     GtkAdjustment *adj = 0;
2682     // fixme: make settable in prefs!
2683     gint mag = 5;
2684     gdouble prop = 0.5;
2685     gboolean flat = FALSE;
2686     gdouble randomized = 0;
2687     gdouble rounded = 0;
2689     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2690     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2692     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2693     gtk_action_set_sensitive( sb2, !flat );
2695     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2696     gtk_adjustment_set_value(adj, mag);
2697     gtk_adjustment_value_changed(adj);
2699     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2700     gtk_adjustment_set_value(adj, prop);
2701     gtk_adjustment_value_changed(adj);
2703     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2704     gtk_adjustment_set_value(adj, rounded);
2705     gtk_adjustment_value_changed(adj);
2707     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2708     gtk_adjustment_set_value(adj, randomized);
2709     gtk_adjustment_value_changed(adj);
2713 void
2714 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2716     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2717     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2718     GtkWidget *l = gtk_label_new(NULL);
2719     gtk_label_set_markup(GTK_LABEL(l), title);
2720     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2721     if ( GTK_IS_TOOLBAR(tbl) ) {
2722         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2723     } else {
2724         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2725     }
2726     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2730 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2732     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2734     {
2735         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2736         ege_output_action_set_use_markup( act, TRUE );
2737         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2738         g_object_set_data( holder, "mode_action", act );
2739     }
2741     {
2742         EgeAdjustmentAction* eact = 0;
2743         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2744         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2746         /* Flatsided checkbox */
2747         {
2748             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2750             GtkTreeIter iter;
2751             gtk_list_store_append( model, &iter );
2752             gtk_list_store_set( model, &iter,
2753                                 0, _("Polygon"),
2754                                 1, _("Regular polygon (with one handle) instead of a star"),
2755                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2756                                 -1 );
2758             gtk_list_store_append( model, &iter );
2759             gtk_list_store_set( model, &iter,
2760                                 0, _("Star"),
2761                                 1, _("Star instead of a regular polygon (with one handle)"),
2762                                 2, INKSCAPE_ICON_DRAW_STAR,
2763                                 -1 );
2765             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2766             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2767             g_object_set_data( holder, "flat_action", act );
2769             ege_select_one_action_set_appearance( act, "full" );
2770             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2771             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2772             ege_select_one_action_set_icon_column( act, 2 );
2773             ege_select_one_action_set_icon_size( act, secondarySize );
2774             ege_select_one_action_set_tooltip_column( act, 1  );
2776             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2777             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2778         }
2780         /* Magnitude */
2781         {
2782         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2783         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2784         eact = create_adjustment_action( "MagnitudeAction",
2785                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2786                                          "/tools/shapes/star/magnitude", 3,
2787                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2788                                          3, 1024, 1, 5,
2789                                          labels, values, G_N_ELEMENTS(labels),
2790                                          sp_stb_magnitude_value_changed,
2791                                          1.0, 0 );
2792         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2793         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2794         }
2796         /* Spoke ratio */
2797         {
2798         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2799         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2800         eact = create_adjustment_action( "SpokeAction",
2801                                          _("Spoke ratio"), _("Spoke ratio:"),
2802                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2803                                          // Base radius is the same for the closest handle.
2804                                          _("Base radius to tip radius ratio"),
2805                                          "/tools/shapes/star/proportion", 0.5,
2806                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2807                                          0.01, 1.0, 0.01, 0.1,
2808                                          labels, values, G_N_ELEMENTS(labels),
2809                                          sp_stb_proportion_value_changed );
2810         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2811         g_object_set_data( holder, "prop_action", eact );
2812         }
2814         if ( !isFlatSided ) {
2815             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2816         } else {
2817             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2818         }
2820         /* Roundedness */
2821         {
2822         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2823         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2824         eact = create_adjustment_action( "RoundednessAction",
2825                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2826                                          "/tools/shapes/star/rounded", 0.0,
2827                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2828                                          -10.0, 10.0, 0.01, 0.1,
2829                                          labels, values, G_N_ELEMENTS(labels),
2830                                          sp_stb_rounded_value_changed );
2831         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2832         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2833         }
2835         /* Randomization */
2836         {
2837         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2838         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2839         eact = create_adjustment_action( "RandomizationAction",
2840                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2841                                          "/tools/shapes/star/randomized", 0.0,
2842                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2843                                          -10.0, 10.0, 0.001, 0.01,
2844                                          labels, values, G_N_ELEMENTS(labels),
2845                                          sp_stb_randomized_value_changed, 0.1, 3 );
2846         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2847         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2848         }
2849     }
2851     {
2852         /* Reset */
2853         {
2854             GtkAction* act = gtk_action_new( "StarResetAction",
2855                                              _("Defaults"),
2856                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2857                                              GTK_STOCK_CLEAR );
2858             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2859             gtk_action_group_add_action( mainActions, act );
2860             gtk_action_set_sensitive( act, TRUE );
2861         }
2862     }
2864     sigc::connection *connection = new sigc::connection(
2865         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2866         );
2867     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2868     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2872 //########################
2873 //##       Rect         ##
2874 //########################
2876 static void sp_rtb_sensitivize( GObject *tbl )
2878     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2879     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2880     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2882     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2883         gtk_action_set_sensitive( not_rounded, FALSE );
2884     } else {
2885         gtk_action_set_sensitive( not_rounded, TRUE );
2886     }
2890 static void
2891 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2892                           void (*setter)(SPRect *, gdouble))
2894     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2896     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2897     SPUnit const *unit = tracker->getActiveUnit();
2899     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2900         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2901         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2902     }
2904     // quit if run by the attr_changed listener
2905     if (g_object_get_data( tbl, "freeze" )) {
2906         return;
2907     }
2909     // in turn, prevent listener from responding
2910     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2912     bool modmade = false;
2913     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2914     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2915         if (SP_IS_RECT(items->data)) {
2916             if (adj->value != 0) {
2917                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2918             } else {
2919                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2920             }
2921             modmade = true;
2922         }
2923     }
2925     sp_rtb_sensitivize( tbl );
2927     if (modmade) {
2928         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2929                                    _("Change rectangle"));
2930     }
2932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2935 static void
2936 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2938     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2941 static void
2942 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2944     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2947 static void
2948 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2950     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2953 static void
2954 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2956     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2961 static void
2962 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2964     GtkAdjustment *adj = 0;
2966     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2967     gtk_adjustment_set_value(adj, 0.0);
2968     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2969     gtk_adjustment_value_changed(adj);
2971     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2972     gtk_adjustment_set_value(adj, 0.0);
2973     gtk_adjustment_value_changed(adj);
2975     sp_rtb_sensitivize( obj );
2978 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2979                                        gchar const */*old_value*/, gchar const */*new_value*/,
2980                                        bool /*is_interactive*/, gpointer data)
2982     GObject *tbl = G_OBJECT(data);
2984     // quit if run by the _changed callbacks
2985     if (g_object_get_data( tbl, "freeze" )) {
2986         return;
2987     }
2989     // in turn, prevent callbacks from responding
2990     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2992     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2993     SPUnit const *unit = tracker->getActiveUnit();
2995     gpointer item = g_object_get_data( tbl, "item" );
2996     if (item && SP_IS_RECT(item)) {
2997         {
2998             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2999             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3000             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3001         }
3003         {
3004             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3005             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3006             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3007         }
3009         {
3010             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3011             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3012             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3013         }
3015         {
3016             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3017             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3018             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3019         }
3020     }
3022     sp_rtb_sensitivize( tbl );
3024     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3028 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3029     NULL, /* child_added */
3030     NULL, /* child_removed */
3031     rect_tb_event_attr_changed,
3032     NULL, /* content_changed */
3033     NULL  /* order_changed */
3034 };
3036 /**
3037  *  \param selection should not be NULL.
3038  */
3039 static void
3040 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3042     int n_selected = 0;
3043     Inkscape::XML::Node *repr = NULL;
3044     SPItem *item = NULL;
3046     if ( g_object_get_data( tbl, "repr" ) ) {
3047         g_object_set_data( tbl, "item", NULL );
3048     }
3049     purge_repr_listener( tbl, tbl );
3051     for (GSList const *items = selection->itemList();
3052          items != NULL;
3053          items = items->next) {
3054         if (SP_IS_RECT((SPItem *) items->data)) {
3055             n_selected++;
3056             item = (SPItem *) items->data;
3057             repr = SP_OBJECT_REPR(item);
3058         }
3059     }
3061     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3063     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3065     if (n_selected == 0) {
3066         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3068         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3069         gtk_action_set_sensitive(w, FALSE);
3070         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3071         gtk_action_set_sensitive(h, FALSE);
3073     } else if (n_selected == 1) {
3074         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3075         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3077         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3078         gtk_action_set_sensitive(w, TRUE);
3079         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3080         gtk_action_set_sensitive(h, TRUE);
3082         if (repr) {
3083             g_object_set_data( tbl, "repr", repr );
3084             g_object_set_data( tbl, "item", item );
3085             Inkscape::GC::anchor(repr);
3086             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3087             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3088         }
3089     } else {
3090         // FIXME: implement averaging of all parameters for multiple selected
3091         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3092         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3093         sp_rtb_sensitivize( tbl );
3094     }
3098 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3100     EgeAdjustmentAction* eact = 0;
3101     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3103     {
3104         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3105         ege_output_action_set_use_markup( act, TRUE );
3106         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3107         g_object_set_data( holder, "mode_action", act );
3108     }
3110     // rx/ry units menu: create
3111     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3112     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3113     // fixme: add % meaning per cent of the width/height
3114     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3115     g_object_set_data( holder, "tracker", tracker );
3117     /* W */
3118     {
3119         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3120         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3121         eact = create_adjustment_action( "RectWidthAction",
3122                                          _("Width"), _("W:"), _("Width of rectangle"),
3123                                          "/tools/shapes/rect/width", 0,
3124                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3125                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3126                                          labels, values, G_N_ELEMENTS(labels),
3127                                          sp_rtb_width_value_changed );
3128         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3129         g_object_set_data( holder, "width_action", eact );
3130         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3131         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3132     }
3134     /* H */
3135     {
3136         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3137         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3138         eact = create_adjustment_action( "RectHeightAction",
3139                                          _("Height"), _("H:"), _("Height of rectangle"),
3140                                          "/tools/shapes/rect/height", 0,
3141                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3142                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3143                                          labels, values, G_N_ELEMENTS(labels),
3144                                          sp_rtb_height_value_changed );
3145         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3146         g_object_set_data( holder, "height_action", eact );
3147         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3148         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3149     }
3151     /* rx */
3152     {
3153         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3154         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3155         eact = create_adjustment_action( "RadiusXAction",
3156                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3157                                          "/tools/shapes/rect/rx", 0,
3158                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3159                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3160                                          labels, values, G_N_ELEMENTS(labels),
3161                                          sp_rtb_rx_value_changed);
3162         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3163         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3164     }
3166     /* ry */
3167     {
3168         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3169         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3170         eact = create_adjustment_action( "RadiusYAction",
3171                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3172                                          "/tools/shapes/rect/ry", 0,
3173                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3174                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3175                                          labels, values, G_N_ELEMENTS(labels),
3176                                          sp_rtb_ry_value_changed);
3177         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3178         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3179     }
3181     // add the units menu
3182     {
3183         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3184         gtk_action_group_add_action( mainActions, act );
3185     }
3187     /* Reset */
3188     {
3189         InkAction* inky = ink_action_new( "RectResetAction",
3190                                           _("Not rounded"),
3191                                           _("Make corners sharp"),
3192                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3193                                           secondarySize );
3194         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3195         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3196         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3197         g_object_set_data( holder, "not_rounded", inky );
3198     }
3200     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3201     sp_rtb_sensitivize( holder );
3203     sigc::connection *connection = new sigc::connection(
3204         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3205         );
3206     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3207     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3210 //########################
3211 //##       3D Box       ##
3212 //########################
3214 // normalize angle so that it lies in the interval [0,360]
3215 static double box3d_normalize_angle (double a) {
3216     double angle = a + ((int) (a/360.0))*360;
3217     if (angle < 0) {
3218         angle += 360.0;
3219     }
3220     return angle;
3223 static void
3224 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3225                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3226     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3227     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3228     //       are reset).
3229     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3231     if (is_infinite) {
3232         gtk_toggle_action_set_active(tact, TRUE);
3233         gtk_action_set_sensitive(act, TRUE);
3235         double angle = persp3d_get_infinite_angle(persp, axis);
3236         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3237             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3238         }
3239     } else {
3240         gtk_toggle_action_set_active(tact, FALSE);
3241         gtk_action_set_sensitive(act, FALSE);
3242     }
3245 static void
3246 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3247     if (!persp_repr) {
3248         g_print ("No perspective given to box3d_resync_toolbar().\n");
3249         return;
3250     }
3252     GtkWidget *tbl = GTK_WIDGET(data);
3253     GtkAdjustment *adj = 0;
3254     GtkAction *act = 0;
3255     GtkToggleAction *tact = 0;
3256     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3257     if (!persp) {
3258         // Hmm, is it an error if this happens?
3259         return;
3260     }
3261     {
3262         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3263         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3264         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3266         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3267     }
3268     {
3269         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3270         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3271         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3273         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3274     }
3275     {
3276         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3277         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3278         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3280         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3281     }
3284 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3285                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3286                                                   bool /*is_interactive*/, gpointer data)
3288     GtkWidget *tbl = GTK_WIDGET(data);
3290     // quit if run by the attr_changed or selection changed listener
3291     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3292         return;
3293     }
3295     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3296     // sp_document_maybe_done() when the document is undo insensitive)
3297     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3299     // TODO: Only update the appropriate part of the toolbar
3300 //    if (!strcmp(name, "inkscape:vp_z")) {
3301         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3302 //    }
3304     Persp3D *persp = persp3d_get_from_repr(repr);
3305     persp3d_update_box_reprs(persp);
3307     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3310 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3312     NULL, /* child_added */
3313     NULL, /* child_removed */
3314     box3d_persp_tb_event_attr_changed,
3315     NULL, /* content_changed */
3316     NULL  /* order_changed */
3317 };
3319 /**
3320  *  \param selection Should not be NULL.
3321  */
3322 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3323 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3324 static void
3325 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3327     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3328     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3329     // update the perspectives with infinite VPs and leave the other ones untouched).
3331     Inkscape::XML::Node *persp_repr = NULL;
3332     purge_repr_listener(tbl, tbl);
3334     SPItem *item = selection->singleItem();
3335     if (item && SP_IS_BOX3D(item)) {
3336         // FIXME: Also deal with multiple selected boxes
3337         SPBox3D *box = SP_BOX3D(item);
3338         Persp3D *persp = box3d_get_perspective(box);
3339         persp_repr = SP_OBJECT_REPR(persp);
3340         if (persp_repr) {
3341             g_object_set_data(tbl, "repr", persp_repr);
3342             Inkscape::GC::anchor(persp_repr);
3343             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3344             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3345         }
3347         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3348         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3349         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3351         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3352         box3d_resync_toolbar(persp_repr, tbl);
3353         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3354     }
3357 static void
3358 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3360     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3361     SPDocument *document = sp_desktop_document(desktop);
3363     // quit if run by the attr_changed or selection changed listener
3364     if (g_object_get_data( dataKludge, "freeze" )) {
3365         return;
3366     }
3368     // in turn, prevent listener from responding
3369     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3371     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3372     if (sel_persps.empty()) {
3373         // this can happen when the document is created; we silently ignore it
3374         return;
3375     }
3376     Persp3D *persp = sel_persps.front();
3378     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3379     SP_OBJECT(persp)->updateRepr();
3381     // TODO: use the correct axis here, too
3382     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3384     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3388 static void
3389 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3391     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3394 static void
3395 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3397     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3400 static void
3401 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3403     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3407 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3409     // TODO: Take all selected perspectives into account
3410     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3411     if (sel_persps.empty()) {
3412         // this can happen when the document is created; we silently ignore it
3413         return;
3414     }
3415     Persp3D *persp = sel_persps.front();
3417     bool set_infinite = gtk_toggle_action_get_active(act);
3418     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3421 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3423     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3426 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3428     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3431 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3433     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3436 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3438     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3439     EgeAdjustmentAction* eact = 0;
3440     SPDocument *document = sp_desktop_document (desktop);
3441     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3443     EgeAdjustmentAction* box3d_angle_x = 0;
3444     EgeAdjustmentAction* box3d_angle_y = 0;
3445     EgeAdjustmentAction* box3d_angle_z = 0;
3447     /* Angle X */
3448     {
3449         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3450         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3451         eact = create_adjustment_action( "3DBoxAngleXAction",
3452                                          _("Angle in X direction"), _("Angle X:"),
3453                                          // Translators: PL is short for 'perspective line'
3454                                          _("Angle of PLs in X direction"),
3455                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3456                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3457                                          -360.0, 360.0, 1.0, 10.0,
3458                                          labels, values, G_N_ELEMENTS(labels),
3459                                          box3d_angle_x_value_changed );
3460         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3461         g_object_set_data( holder, "box3d_angle_x_action", eact );
3462         box3d_angle_x = eact;
3463     }
3465     if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3466         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3467     } else {
3468         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3469     }
3472     /* VP X state */
3473     {
3474         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3475                                                       // Translators: VP is short for 'vanishing point'
3476                                                       _("State of VP in X direction"),
3477                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3478                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3479                                                       Inkscape::ICON_SIZE_DECORATION );
3480         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3481         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3482         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3483         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3484         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3485     }
3487     /* Angle Y */
3488     {
3489         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3490         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3491         eact = create_adjustment_action( "3DBoxAngleYAction",
3492                                          _("Angle in Y direction"), _("Angle Y:"),
3493                                          // Translators: PL is short for 'perspective line'
3494                                          _("Angle of PLs in Y direction"),
3495                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3496                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3497                                          -360.0, 360.0, 1.0, 10.0,
3498                                          labels, values, G_N_ELEMENTS(labels),
3499                                          box3d_angle_y_value_changed );
3500         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3501         g_object_set_data( holder, "box3d_angle_y_action", eact );
3502         box3d_angle_y = eact;
3503     }
3505     if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3506         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3507     } else {
3508         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3509     }
3511     /* VP Y state */
3512     {
3513         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3514                                                       // Translators: VP is short for 'vanishing point'
3515                                                       _("State of VP in Y direction"),
3516                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3517                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3518                                                       Inkscape::ICON_SIZE_DECORATION );
3519         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3520         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3521         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3522         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3523         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3524     }
3526     /* Angle Z */
3527     {
3528         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3529         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3530         eact = create_adjustment_action( "3DBoxAngleZAction",
3531                                          _("Angle in Z direction"), _("Angle Z:"),
3532                                          // Translators: PL is short for 'perspective line'
3533                                          _("Angle of PLs in Z direction"),
3534                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3535                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3536                                          -360.0, 360.0, 1.0, 10.0,
3537                                          labels, values, G_N_ELEMENTS(labels),
3538                                          box3d_angle_z_value_changed );
3539         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3540         g_object_set_data( holder, "box3d_angle_z_action", eact );
3541         box3d_angle_z = eact;
3542     }
3544     if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3545         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3546     } else {
3547         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3548     }
3550     /* VP Z state */
3551     {
3552         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3553                                                       // Translators: VP is short for 'vanishing point'
3554                                                       _("State of VP in Z direction"),
3555                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3556                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3557                                                       Inkscape::ICON_SIZE_DECORATION );
3558         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3559         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3560         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3561         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3562         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3563     }
3565     sigc::connection *connection = new sigc::connection(
3566         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3567        );
3568     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3569     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3572 //########################
3573 //##       Spiral       ##
3574 //########################
3576 static void
3577 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3579     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3581     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3582         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3583         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3584     }
3586     // quit if run by the attr_changed listener
3587     if (g_object_get_data( tbl, "freeze" )) {
3588         return;
3589     }
3591     // in turn, prevent listener from responding
3592     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3594     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3596     bool modmade = false;
3597     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3598          items != NULL;
3599          items = items->next)
3600     {
3601         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3602             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3603             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3604             SP_OBJECT((SPItem *) items->data)->updateRepr();
3605             modmade = true;
3606         }
3607     }
3609     g_free(namespaced_name);
3611     if (modmade) {
3612         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3613                                    _("Change spiral"));
3614     }
3616     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3619 static void
3620 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3622     sp_spl_tb_value_changed(adj, tbl, "revolution");
3625 static void
3626 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3628     sp_spl_tb_value_changed(adj, tbl, "expansion");
3631 static void
3632 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3634     sp_spl_tb_value_changed(adj, tbl, "t0");
3637 static void
3638 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3640     GtkWidget *tbl = GTK_WIDGET(obj);
3642     GtkAdjustment *adj;
3644     // fixme: make settable
3645     gdouble rev = 5;
3646     gdouble exp = 1.0;
3647     gdouble t0 = 0.0;
3649     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3650     gtk_adjustment_set_value(adj, rev);
3651     gtk_adjustment_value_changed(adj);
3653     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3654     gtk_adjustment_set_value(adj, exp);
3655     gtk_adjustment_value_changed(adj);
3657     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3658     gtk_adjustment_set_value(adj, t0);
3659     gtk_adjustment_value_changed(adj);
3661     spinbutton_defocus(GTK_OBJECT(tbl));
3665 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3666                                          gchar const */*old_value*/, gchar const */*new_value*/,
3667                                          bool /*is_interactive*/, gpointer data)
3669     GtkWidget *tbl = GTK_WIDGET(data);
3671     // quit if run by the _changed callbacks
3672     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3673         return;
3674     }
3676     // in turn, prevent callbacks from responding
3677     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3679     GtkAdjustment *adj;
3680     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3681     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3683     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3684     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3686     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3687     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3689     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3693 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3694     NULL, /* child_added */
3695     NULL, /* child_removed */
3696     spiral_tb_event_attr_changed,
3697     NULL, /* content_changed */
3698     NULL  /* order_changed */
3699 };
3701 static void
3702 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3704     int n_selected = 0;
3705     Inkscape::XML::Node *repr = NULL;
3707     purge_repr_listener( tbl, tbl );
3709     for (GSList const *items = selection->itemList();
3710          items != NULL;
3711          items = items->next)
3712     {
3713         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3714             n_selected++;
3715             repr = SP_OBJECT_REPR((SPItem *) items->data);
3716         }
3717     }
3719     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3721     if (n_selected == 0) {
3722         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3723     } else if (n_selected == 1) {
3724         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3726         if (repr) {
3727             g_object_set_data( tbl, "repr", repr );
3728             Inkscape::GC::anchor(repr);
3729             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3730             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3731         }
3732     } else {
3733         // FIXME: implement averaging of all parameters for multiple selected
3734         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3735         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3736     }
3740 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3742     EgeAdjustmentAction* eact = 0;
3743     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3745     {
3746         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3747         ege_output_action_set_use_markup( act, TRUE );
3748         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3749         g_object_set_data( holder, "mode_action", act );
3750     }
3752     /* Revolution */
3753     {
3754         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3755         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3756         eact = create_adjustment_action( "SpiralRevolutionAction",
3757                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3758                                          "/tools/shapes/spiral/revolution", 3.0,
3759                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3760                                          0.01, 1024.0, 0.1, 1.0,
3761                                          labels, values, G_N_ELEMENTS(labels),
3762                                          sp_spl_tb_revolution_value_changed, 1, 2);
3763         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3764     }
3766     /* Expansion */
3767     {
3768         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3769         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3770         eact = create_adjustment_action( "SpiralExpansionAction",
3771                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3772                                          "/tools/shapes/spiral/expansion", 1.0,
3773                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3774                                          0.0, 1000.0, 0.01, 1.0,
3775                                          labels, values, G_N_ELEMENTS(labels),
3776                                          sp_spl_tb_expansion_value_changed);
3777         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3778     }
3780     /* T0 */
3781     {
3782         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3783         gdouble values[] = {0, 0.5, 0.9};
3784         eact = create_adjustment_action( "SpiralT0Action",
3785                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3786                                          "/tools/shapes/spiral/t0", 0.0,
3787                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3788                                          0.0, 0.999, 0.01, 1.0,
3789                                          labels, values, G_N_ELEMENTS(labels),
3790                                          sp_spl_tb_t0_value_changed);
3791         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3792     }
3794     /* Reset */
3795     {
3796         InkAction* inky = ink_action_new( "SpiralResetAction",
3797                                           _("Defaults"),
3798                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3799                                           GTK_STOCK_CLEAR,
3800                                           secondarySize );
3801         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3802         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3803     }
3806     sigc::connection *connection = new sigc::connection(
3807         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3808         );
3809     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3810     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3813 //########################
3814 //##     Pen/Pencil     ##
3815 //########################
3817 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3818 static Glib::ustring const
3819 freehand_tool_name(GObject *dataKludge)
3821     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3822     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3823              ? "/tools/freehand/pen"
3824              : "/tools/freehand/pencil" );
3827 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3829     gint mode = ege_select_one_action_get_active(act);
3831     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3832     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3834     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3836     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3837     // preparatory work here
3838     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3839         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3840         sp_pen_context_set_polyline_mode(pc);
3841     }
3844 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3846     /* Freehand mode toggle buttons */
3847     {
3848         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3849         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3850         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3852         {
3853             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3855             GtkTreeIter iter;
3856             gtk_list_store_append( model, &iter );
3857             gtk_list_store_set( model, &iter,
3858                                 0, _("Bezier"),
3859                                 1, _("Create regular Bezier path"),
3860                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3861                                 -1 );
3863             gtk_list_store_append( model, &iter );
3864             gtk_list_store_set( model, &iter,
3865                                 0, _("Spiro"),
3866                                 1, _("Create Spiro path"),
3867                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3868                                 -1 );
3870             if (!tool_is_pencil) {
3871                 gtk_list_store_append( model, &iter );
3872                 gtk_list_store_set( model, &iter,
3873                                     0, _("Zigzag"),
3874                                     1, _("Create a sequence of straight line segments"),
3875                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3876                                     -1 );
3878                 gtk_list_store_append( model, &iter );
3879                 gtk_list_store_set( model, &iter,
3880                                     0, _("Paraxial"),
3881                                     1, _("Create a sequence of paraxial line segments"),
3882                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3883                                     -1 );
3884             }
3886             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3887                                                                 "FreehandModeActionPencil" :
3888                                                                 "FreehandModeActionPen",
3889                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3890             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3892             ege_select_one_action_set_appearance( act, "full" );
3893             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3894             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3895             ege_select_one_action_set_icon_column( act, 2 );
3896             ege_select_one_action_set_icon_size( act, secondarySize );
3897             ege_select_one_action_set_tooltip_column( act, 1  );
3899             ege_select_one_action_set_active( act, freehandMode);
3900             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3901         }
3902     }
3905 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3906     gint shape = ege_select_one_action_get_active( act );
3907     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3908     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3911 /**
3912  * \brief Generate the list of freehand advanced shape option entries.
3913  */
3914 GList * freehand_shape_dropdown_items_list() {
3915     GList *glist = NULL;
3917     glist = g_list_append (glist, _("None"));
3918     glist = g_list_append (glist, _("Triangle in"));
3919     glist = g_list_append (glist, _("Triangle out"));
3920     glist = g_list_append (glist, _("Ellipse"));
3921     glist = g_list_append (glist, _("From clipboard"));
3923     return glist;
3926 static void
3927 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3928     /*advanced shape options */
3929     {
3930         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3931         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3933         GList* items = 0;
3934         gint count = 0;
3935         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3936         {
3937             GtkTreeIter iter;
3938             gtk_list_store_append( model, &iter );
3939             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3940             count++;
3941         }
3942         g_list_free( items );
3943         items = 0;
3944         EgeSelectOneAction* act1 = ege_select_one_action_new(
3945             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3946             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3947         g_object_set( act1, "short_label", _("Shape:"), NULL );
3948         ege_select_one_action_set_appearance( act1, "compact" );
3949         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3950         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3951         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3952         g_object_set_data( holder, "shape_action", act1 );
3953     }
3956 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3958     sp_add_freehand_mode_toggle(mainActions, holder, false);
3959     freehand_add_advanced_shape_options(mainActions, holder, false);
3963 static void
3964 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3966     GtkWidget *tbl = GTK_WIDGET(obj);
3968     GtkAdjustment *adj;
3970     // fixme: make settable
3971     gdouble tolerance = 4;
3973     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3974     gtk_adjustment_set_value(adj, tolerance);
3975     gtk_adjustment_value_changed(adj);
3977     spinbutton_defocus(GTK_OBJECT(tbl));
3980 static void
3981 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3983     // quit if run by the attr_changed listener
3984     if (g_object_get_data( tbl, "freeze" )) {
3985         return;
3986     }
3987     // in turn, prevent listener from responding
3988     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3989     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3990     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3991     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3994 /*
3995 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3996 public:
3997     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3998     {
3999         g_object_set_data(_obj, "prefobserver", this);
4000     }
4001     virtual ~PencilToleranceObserver() {
4002         if (g_object_get_data(_obj, "prefobserver") == this) {
4003                 g_object_set_data(_obj, "prefobserver", NULL);
4004         }
4005     }
4006     virtual void notify(Inkscape::Preferences::Entry const &val) {
4007         GObject* tbl = _obj;
4008         if (g_object_get_data( tbl, "freeze" )) {
4009             return;
4010         }
4011         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4013         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4015         double v = val.getDouble(adj->value);
4016         gtk_adjustment_set_value(adj, v);
4017         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4018     }
4019 private:
4020     GObject *_obj;
4021 };
4022 */
4024 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4026     sp_add_freehand_mode_toggle(mainActions, holder, true);
4028     EgeAdjustmentAction* eact = 0;
4030     /* Tolerance */
4031     {
4032         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4033         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4034         eact = create_adjustment_action( "PencilToleranceAction",
4035                                          _("Smoothing:"), _("Smoothing: "),
4036                  _("How much smoothing (simplifying) is applied to the line"),
4037                                          "/tools/freehand/pencil/tolerance",
4038                                          3.0,
4039                                          GTK_WIDGET(desktop->canvas), NULL,
4040                                          holder, TRUE, "altx-pencil",
4041                                          1, 100.0, 0.5, 1.0,
4042                                          labels, values, G_N_ELEMENTS(labels),
4043                                          sp_pencil_tb_tolerance_value_changed,
4044                                          1, 2);
4045         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4046         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4047     }
4049     /* advanced shape options */
4050     freehand_add_advanced_shape_options(mainActions, holder, true);
4052     /* Reset */
4053     {
4054         InkAction* inky = ink_action_new( "PencilResetAction",
4055                                           _("Defaults"),
4056                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4057                                           GTK_STOCK_CLEAR,
4058                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4059         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4060         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4061     }
4063     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4068 //########################
4069 //##       Tweak        ##
4070 //########################
4072 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4074     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4075     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4078 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4080     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4081     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4084 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4086     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4087     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4090 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4092     int mode = ege_select_one_action_get_active( act );
4093     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4094     prefs->setInt("/tools/tweak/mode", mode);
4096     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4097     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4098     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4099     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4100     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4101     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4102     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4103         if (doh) gtk_action_set_sensitive (doh, TRUE);
4104         if (dos) gtk_action_set_sensitive (dos, TRUE);
4105         if (dol) gtk_action_set_sensitive (dol, TRUE);
4106         if (doo) gtk_action_set_sensitive (doo, TRUE);
4107         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4108         if (fid) gtk_action_set_sensitive (fid, FALSE);
4109     } else {
4110         if (doh) gtk_action_set_sensitive (doh, FALSE);
4111         if (dos) gtk_action_set_sensitive (dos, FALSE);
4112         if (dol) gtk_action_set_sensitive (dol, FALSE);
4113         if (doo) gtk_action_set_sensitive (doo, FALSE);
4114         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4115         if (fid) gtk_action_set_sensitive (fid, TRUE);
4116     }
4119 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4121     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4122     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4125 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4126     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4127     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4129 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4130     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4131     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4133 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4134     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4135     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4137 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4138     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4139     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4142 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4144     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4145     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4147     {
4148         /* Width */
4149         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4150         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4151         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4152                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4153                                                               "/tools/tweak/width", 15,
4154                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4155                                                               1, 100, 1.0, 10.0,
4156                                                               labels, values, G_N_ELEMENTS(labels),
4157                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4158         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4159         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4160         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4161     }
4164     {
4165         /* Force */
4166         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4167         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4168         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4169                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4170                                                               "/tools/tweak/force", 20,
4171                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4172                                                               1, 100, 1.0, 10.0,
4173                                                               labels, values, G_N_ELEMENTS(labels),
4174                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4175         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4176         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4177         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4178     }
4180     /* Mode */
4181     {
4182         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4184         GtkTreeIter iter;
4185         gtk_list_store_append( model, &iter );
4186         gtk_list_store_set( model, &iter,
4187                             0, _("Move mode"),
4188                             1, _("Move objects in any direction"),
4189                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4190                             -1 );
4192         gtk_list_store_append( model, &iter );
4193         gtk_list_store_set( model, &iter,
4194                             0, _("Move in/out mode"),
4195                             1, _("Move objects towards cursor; with Shift from cursor"),
4196                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4197                             -1 );
4199         gtk_list_store_append( model, &iter );
4200         gtk_list_store_set( model, &iter,
4201                             0, _("Move jitter mode"),
4202                             1, _("Move objects in random directions"),
4203                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4204                             -1 );
4206         gtk_list_store_append( model, &iter );
4207         gtk_list_store_set( model, &iter,
4208                             0, _("Scale mode"),
4209                             1, _("Shrink objects, with Shift enlarge"),
4210                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4211                             -1 );
4213         gtk_list_store_append( model, &iter );
4214         gtk_list_store_set( model, &iter,
4215                             0, _("Rotate mode"),
4216                             1, _("Rotate objects, with Shift counterclockwise"),
4217                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4218                             -1 );
4220         gtk_list_store_append( model, &iter );
4221         gtk_list_store_set( model, &iter,
4222                             0, _("Duplicate/delete mode"),
4223                             1, _("Duplicate objects, with Shift delete"),
4224                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4225                             -1 );
4227         gtk_list_store_append( model, &iter );
4228         gtk_list_store_set( model, &iter,
4229                             0, _("Push mode"),
4230                             1, _("Push parts of paths in any direction"),
4231                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4232                             -1 );
4234         gtk_list_store_append( model, &iter );
4235         gtk_list_store_set( model, &iter,
4236                             0, _("Shrink/grow mode"),
4237                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4238                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4239                             -1 );
4241         gtk_list_store_append( model, &iter );
4242         gtk_list_store_set( model, &iter,
4243                             0, _("Attract/repel mode"),
4244                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4245                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4246                             -1 );
4248         gtk_list_store_append( model, &iter );
4249         gtk_list_store_set( model, &iter,
4250                             0, _("Roughen mode"),
4251                             1, _("Roughen parts of paths"),
4252                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4253                             -1 );
4255         gtk_list_store_append( model, &iter );
4256         gtk_list_store_set( model, &iter,
4257                             0, _("Color paint mode"),
4258                             1, _("Paint the tool's color upon selected objects"),
4259                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4260                             -1 );
4262         gtk_list_store_append( model, &iter );
4263         gtk_list_store_set( model, &iter,
4264                             0, _("Color jitter mode"),
4265                             1, _("Jitter the colors of selected objects"),
4266                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4267                             -1 );
4269         gtk_list_store_append( model, &iter );
4270         gtk_list_store_set( model, &iter,
4271                             0, _("Blur mode"),
4272                             1, _("Blur selected objects more; with Shift, blur less"),
4273                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4274                             -1 );
4277         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4278         g_object_set( act, "short_label", _("Mode:"), NULL );
4279         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4280         g_object_set_data( holder, "mode_action", act );
4282         ege_select_one_action_set_appearance( act, "full" );
4283         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4284         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4285         ege_select_one_action_set_icon_column( act, 2 );
4286         ege_select_one_action_set_icon_size( act, secondarySize );
4287         ege_select_one_action_set_tooltip_column( act, 1  );
4289         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4290         ege_select_one_action_set_active( act, mode );
4291         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4293         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4294     }
4296     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4298     {
4299         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4300         ege_output_action_set_use_markup( act, TRUE );
4301         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4302         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4303             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4304         g_object_set_data( holder, "tweak_channels_label", act);
4305     }
4307     {
4308         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4309                                                       _("Hue"),
4310                                                       _("In color mode, act on objects' hue"),
4311                                                       NULL,
4312                                                       Inkscape::ICON_SIZE_DECORATION );
4313         //TRANSLATORS:  "H" here stands for hue
4314         g_object_set( act, "short_label", _("H"), NULL );
4315         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4316         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4317         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4318         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4319             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4320         g_object_set_data( holder, "tweak_doh", act);
4321     }
4322     {
4323         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4324                                                       _("Saturation"),
4325                                                       _("In color mode, act on objects' saturation"),
4326                                                       NULL,
4327                                                       Inkscape::ICON_SIZE_DECORATION );
4328         //TRANSLATORS: "S" here stands for Saturation
4329         g_object_set( act, "short_label", _("S"), NULL );
4330         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4331         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4332         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4333         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4334             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4335         g_object_set_data( holder, "tweak_dos", act );
4336     }
4337     {
4338         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4339                                                       _("Lightness"),
4340                                                       _("In color mode, act on objects' lightness"),
4341                                                       NULL,
4342                                                       Inkscape::ICON_SIZE_DECORATION );
4343         //TRANSLATORS: "L" here stands for Lightness
4344         g_object_set( act, "short_label", _("L"), NULL );
4345         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4346         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4347         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4348         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4349             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4350         g_object_set_data( holder, "tweak_dol", act );
4351     }
4352     {
4353         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4354                                                       _("Opacity"),
4355                                                       _("In color mode, act on objects' opacity"),
4356                                                       NULL,
4357                                                       Inkscape::ICON_SIZE_DECORATION );
4358         //TRANSLATORS: "O" here stands for Opacity
4359         g_object_set( act, "short_label", _("O"), NULL );
4360         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4361         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4362         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4363         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4364             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4365         g_object_set_data( holder, "tweak_doo", act );
4366     }
4368     {   /* Fidelity */
4369         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4370         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4371         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4372                                                               _("Fidelity"), _("Fidelity:"),
4373                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4374                                                               "/tools/tweak/fidelity", 50,
4375                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4376                                                               1, 100, 1.0, 10.0,
4377                                                               labels, values, G_N_ELEMENTS(labels),
4378                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4379         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4380         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4381         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4382             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4383         g_object_set_data( holder, "tweak_fidelity", eact );
4384     }
4387     /* Use Pressure button */
4388     {
4389         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4390                                                       _("Pressure"),
4391                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4392                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4393                                                       Inkscape::ICON_SIZE_DECORATION );
4394         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4395         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4396         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4397     }
4402 //########################
4403 //##       Spray        ##
4404 //########################
4406 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4408     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4409     prefs->setDouble( "/tools/spray/width", adj->value );
4412 /*
4413 static void sp_spray_force_value_changed( GtkAdjustment * / *adj* /, GObject * / *tbl* / )
4415     //Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4416     //prefs->setDouble( "/tools/spray/force", adj->value * 0.01 );
4418 */
4420 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4422     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4423     prefs->setDouble( "/tools/spray/mean", adj->value );
4426 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4428     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4429     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4432 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4434     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4435     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4438 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4440     int mode = ege_select_one_action_get_active( act );
4441     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4442     prefs->setInt("/tools/spray/mode", mode);
4445 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4447     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4448     prefs->setDouble( "/tools/spray/population", adj->value );
4451 /*static void spray_toggle_doh (GtkToggleAction *act, gpointer ) {
4452     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4453     prefs->setBool("/tools/spray/doh", gtk_toggle_action_get_active(act));
4455 static void spray_toggle_dos (GtkToggleAction *act, gpointer ) {
4456     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4457     prefs->setBool("/tools/spray/dos", gtk_toggle_action_get_active(act));
4459 static void spray_toggle_dol (GtkToggleAction *act, gpointer ) {
4460     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4461     prefs->setBool("/tools/spray/dol", gtk_toggle_action_get_active(act));
4463 static void spray_toggle_doo (GtkToggleAction *act, gpointer ) {
4464     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4465     prefs->setBool("/tools/spray/doo", gtk_toggle_action_get_active(act));
4467 */
4468 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4470     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4471     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4473     {
4474         /* Width */
4475         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4476         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4477         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4478                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4479                                                               "/tools/spray/width", 15,
4480                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4481                                                               1, 100, 1.0, 10.0,
4482                                                               labels, values, G_N_ELEMENTS(labels),
4483                                                               sp_spray_width_value_changed,  1, 0 );
4484         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4485         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4486         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4487     }
4489     {
4490         /* Mean */
4491         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4492         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4493         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4494                                                               _("Mean"), _("Mean:"), _("The mean of the spray action"),
4495                                                               "/tools/spray/mean", 20,
4496                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4497                                                               1, 100, 1.0, 10.0,
4498                                                               labels, values, G_N_ELEMENTS(labels),
4499                                                               sp_spray_mean_value_changed,  1, 0 );
4500         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4501         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4502         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4503     }
4505     {
4506         /* Standard_deviation */
4507         gchar const* labels[] = {_("(minimum standard_deviation)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum standard_deviation)")};
4508         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4509         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4510                                                               _("SD"), _("SD:"), _("The standard deviation of the spray action"),
4511                                                               "/tools/spray/standard_deviation", 20,
4512                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4513                                                               1, 100, 1.0, 10.0,
4514                                                               labels, values, G_N_ELEMENTS(labels),
4515                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4516         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4517         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4518         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4519     }
4521     /* Mode */
4522     {
4523         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4525         GtkTreeIter iter;
4526         gtk_list_store_append( model, &iter );
4527         gtk_list_store_set( model, &iter,
4528                             0, _("Spray with copies"),
4529                             1, _("Spray copies of the initial selection"),
4530                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4531                             -1 );
4533         gtk_list_store_append( model, &iter );
4534         gtk_list_store_set( model, &iter,
4535                             0, _("Spray with clones"),
4536                             1, _("Spray clones of the initial selection"),
4537                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4538                             -1 );
4540         gtk_list_store_append( model, &iter );
4541         gtk_list_store_set( model, &iter,
4542                             0, _("Spray single path"),
4543                             1, _("Spray objects in a single path"),
4544                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4545                             -1 );
4547         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4548         g_object_set( act, "short_label", _("Mode:"), NULL );
4549         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4550         g_object_set_data( holder, "mode_action", act );
4552         ege_select_one_action_set_appearance( act, "full" );
4553         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4554         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4555         ege_select_one_action_set_icon_column( act, 2 );
4556         ege_select_one_action_set_icon_size( act, secondarySize );
4557         ege_select_one_action_set_tooltip_column( act, 1  );
4559         gint mode = prefs->getInt("/tools/spray/mode", 0);
4560         ege_select_one_action_set_active( act, mode );
4561         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4563         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4564     }
4566     {   /* Population */
4567         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4568         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4569         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4570                                                               _("Population"), _("Population:"),
4571                                                               _("This setting adjusts the number of items sprayed"),
4572                                                               "/tools/spray/population", 50,
4573                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4574                                                               1, 100, 1.0, 10.0,
4575                                                               labels, values, G_N_ELEMENTS(labels),
4576                                                               sp_spray_population_value_changed,  1, 0 );
4577         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4578         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4579         g_object_set_data( holder, "spray_population", eact );
4580     }
4582     /* Use Pressure button */
4583     {
4584         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4585                                                       _("Pressure"),
4586                                                       _("Use the pressure of the input device to alter the force of spray action"),
4587                                                       "use_pressure",
4588                                                       Inkscape::ICON_SIZE_DECORATION );
4589         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4590         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4591         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4592     }
4596 //########################
4597 //##     Calligraphy    ##
4598 //########################
4599 static void update_presets_list (GObject *tbl)
4601     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4602     if (g_object_get_data(tbl, "presets_blocked"))
4603         return;
4605     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4606     if (!sel) {
4607         // WTF!? This will cause a segfault if ever reached
4608         //ege_select_one_action_set_active(sel, 0);
4609         return;
4610     }
4612     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4614     int ege_index = 1;
4615     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4616         bool match = true;
4618         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4619         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4620             Glib::ustring entry_name = j->getEntryName();
4621             if (entry_name == "id" || entry_name == "name") continue;
4623             void *widget = g_object_get_data(tbl, entry_name.data());
4624             if (widget) {
4625                 if (GTK_IS_ADJUSTMENT(widget)) {
4626                     double v = j->getDouble();
4627                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4628                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4629                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4630                         match = false;
4631                         break;
4632                     }
4633                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4634                     bool v = j->getBool();
4635                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4636                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4637                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4638                         match = false;
4639                         break;
4640                     }
4641                 }
4642             }
4643         }
4645         if (match) {
4646             // newly added item is at the same index as the
4647             // save command, so we need to change twice for it to take effect
4648             ege_select_one_action_set_active(sel, 0);
4649             ege_select_one_action_set_active(sel, ege_index); // one-based index
4650             return;
4651         }
4652     }
4654     // no match found
4655     ege_select_one_action_set_active(sel, 0);
4658 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4660     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4661     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4662     update_presets_list(tbl);
4665 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4667     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4668     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4669     update_presets_list(tbl);
4672 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4674     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4675     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4676     update_presets_list(tbl);
4679 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4681     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4682     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4683     update_presets_list(tbl);
4686 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4688     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4689     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4690     update_presets_list(tbl);
4693 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4695     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4696     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4697     update_presets_list(tbl);
4700 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4702     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4703     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4704     update_presets_list(tbl);
4707 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4709     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4710     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4711     update_presets_list(tbl);
4714 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4716     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4717     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4718     update_presets_list(tbl);
4721 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4723     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4724     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4725     update_presets_list(tbl);
4728 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4730     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4731     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4732     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4733     update_presets_list(tbl);
4734     if (calligraphy_angle )
4735         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4739 static gchar const *const widget_names[] = {
4740     "width",
4741     "mass",
4742     "wiggle",
4743     "angle",
4744     "thinning",
4745     "tremor",
4746     "flatness",
4747     "cap_rounding",
4748     "usepressure",
4749     "tracebackground",
4750     "usetilt"
4751 };
4754 static void sp_dcc_build_presets_list(GObject *tbl)
4756     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4758     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4759     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4760     gtk_list_store_clear (model);
4762     {
4763         GtkTreeIter iter;
4764         gtk_list_store_append( model, &iter );
4765         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4766     }
4768     // iterate over all presets to populate the list
4769     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4770     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4771     int ii=1;
4773     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4774         GtkTreeIter iter;
4775         Glib::ustring preset_name = prefs->getString(*i + "/name");
4776         gtk_list_store_append( model, &iter );
4777         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4778     }
4780     {
4781         GtkTreeIter iter;
4782         gtk_list_store_append( model, &iter );
4783         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4784         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4785     }
4787     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4789     update_presets_list (tbl);
4792 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4794     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4795     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4796     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4797     if (! desktop) return;
4799     if (g_object_get_data(tbl, "presets_blocked"))
4800         return;
4802     CalligraphicProfileRename::show(desktop);
4803     if ( !CalligraphicProfileRename::applied()) {
4804         // dialog cancelled
4805         update_presets_list (tbl);
4806         return;
4807     }
4808     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4810     if (profile_name.empty()) {
4811         // empty name entered
4812         update_presets_list (tbl);
4813         return;
4814     }
4816     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4818     // If there's a preset with the given name, find it and set save_path appropriately
4819     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4820     int total_presets = presets.size();
4821     int new_index = -1;
4822     Glib::ustring save_path; // profile pref path without a trailing slash
4824     int temp_index = 0;
4825     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4826         Glib::ustring name = prefs->getString(*i + "/name");
4827         if (!name.empty() && profile_name == name) {
4828             new_index = temp_index;
4829             save_path = *i;
4830             break;
4831         }
4832     }
4834     if (new_index == -1) {
4835         // no preset with this name, create
4836         new_index = total_presets + 1;
4837         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4838         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4839         g_free(profile_id);
4840     }
4842     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4843         gchar const *const widget_name = widget_names[i];
4844         void *widget = g_object_get_data(tbl, widget_name);
4845         if (widget) {
4846             if (GTK_IS_ADJUSTMENT(widget)) {
4847                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4848                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4849                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4850             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4851                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4852                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4853                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4854             } else {
4855                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4856             }
4857         } else {
4858             g_warning("Bad key when writing preset: %s\n", widget_name);
4859         }
4860     }
4861     prefs->setString(save_path + "/name", profile_name);
4863     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4864     sp_dcc_build_presets_list (tbl);
4868 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4870     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4872     gint preset_index = ege_select_one_action_get_active( act );
4873     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4874     // even when the preset is not changed. It would be good to replace it with something more
4875     // modern. Index 0 means "No preset", so we don't do anything.
4876     if (preset_index == 0) return;
4878     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4880     if (preset_index == save_presets_index) {
4881         // this is the Save command
4882         sp_dcc_save_profile(NULL, tbl);
4883         return;
4884     }
4886     if (g_object_get_data(tbl, "presets_blocked"))
4887         return;
4889     // preset_index is one-based so we subtract 1
4890     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4891     Glib::ustring preset_path = presets.at(preset_index - 1);
4893     if (!preset_path.empty()) {
4894         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
4896         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4898         // Shouldn't this be std::map?
4899         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4900             Glib::ustring entry_name = i->getEntryName();
4901             if (entry_name == "id" || entry_name == "name") continue;
4902             void *widget = g_object_get_data(tbl, entry_name.data());
4903             if (widget) {
4904                 if (GTK_IS_ADJUSTMENT(widget)) {
4905                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4906                     gtk_adjustment_set_value(adj, i->getDouble());
4907                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4908                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4909                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4910                     gtk_toggle_action_set_active(toggle, i->getBool());
4911                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4912                 } else {
4913                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4914                 }
4915             } else {
4916                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4917             }
4918         }
4919         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4920     }
4924 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4926     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4927     {
4928         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4930         EgeAdjustmentAction* calligraphy_angle = 0;
4932         {
4933         /* Width */
4934         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4935         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4936         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4937                                                               _("Pen Width"), _("Width:"),
4938                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4939                                                               "/tools/calligraphic/width", 15,
4940                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4941                                                               1, 100, 1.0, 10.0,
4942                                                               labels, values, G_N_ELEMENTS(labels),
4943                                                               sp_ddc_width_value_changed,  1, 0 );
4944         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4945         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4946         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4947         }
4949         {
4950         /* Thinning */
4951             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4952             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4953         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4954                                                               _("Stroke Thinning"), _("Thinning:"),
4955                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4956                                                               "/tools/calligraphic/thinning", 10,
4957                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4958                                                               -100, 100, 1, 10.0,
4959                                                               labels, values, G_N_ELEMENTS(labels),
4960                                                               sp_ddc_velthin_value_changed, 1, 0);
4961         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4962         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4963         }
4965         {
4966         /* Angle */
4967         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4968         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4969         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4970                                                               _("Pen Angle"), _("Angle:"),
4971                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4972                                                               "/tools/calligraphic/angle", 30,
4973                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4974                                                               -90.0, 90.0, 1.0, 10.0,
4975                                                               labels, values, G_N_ELEMENTS(labels),
4976                                                               sp_ddc_angle_value_changed, 1, 0 );
4977         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4978         g_object_set_data( holder, "angle_action", eact );
4979         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4980         calligraphy_angle = eact;
4981         }
4983         {
4984         /* Fixation */
4985             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4986         gdouble values[] = {0, 20, 40, 60, 90, 100};
4987         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4988                                                               _("Fixation"), _("Fixation:"),
4989                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4990                                                               "/tools/calligraphic/flatness", 90,
4991                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4992                                                               0.0, 100, 1.0, 10.0,
4993                                                               labels, values, G_N_ELEMENTS(labels),
4994                                                               sp_ddc_flatness_value_changed, 1, 0);
4995         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4996         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4997         }
4999         {
5000         /* Cap Rounding */
5001             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5002         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5003         // TRANSLATORS: "cap" means "end" (both start and finish) here
5004         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5005                                                               _("Cap rounding"), _("Caps:"),
5006                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5007                                                               "/tools/calligraphic/cap_rounding", 0.0,
5008                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5009                                                               0.0, 5.0, 0.01, 0.1,
5010                                                               labels, values, G_N_ELEMENTS(labels),
5011                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5012         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5013         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5014         }
5016         {
5017         /* Tremor */
5018             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5019         gdouble values[] = {0, 10, 20, 40, 60, 100};
5020         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5021                                                               _("Stroke Tremor"), _("Tremor:"),
5022                                                               _("Increase to make strokes rugged and trembling"),
5023                                                               "/tools/calligraphic/tremor", 0.0,
5024                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5025                                                               0.0, 100, 1, 10.0,
5026                                                               labels, values, G_N_ELEMENTS(labels),
5027                                                               sp_ddc_tremor_value_changed, 1, 0);
5029         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5030         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5031         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5032         }
5034         {
5035         /* Wiggle */
5036         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5037         gdouble values[] = {0, 20, 40, 60, 100};
5038         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5039                                                               _("Pen Wiggle"), _("Wiggle:"),
5040                                                               _("Increase to make the pen waver and wiggle"),
5041                                                               "/tools/calligraphic/wiggle", 0.0,
5042                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5043                                                               0.0, 100, 1, 10.0,
5044                                                               labels, values, G_N_ELEMENTS(labels),
5045                                                               sp_ddc_wiggle_value_changed, 1, 0);
5046         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5047         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5048         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5049         }
5051         {
5052         /* Mass */
5053             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5054         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5055         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5056                                                               _("Pen Mass"), _("Mass:"),
5057                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5058                                                               "/tools/calligraphic/mass", 2.0,
5059                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5060                                                               0.0, 100, 1, 10.0,
5061                                                               labels, values, G_N_ELEMENTS(labels),
5062                                                               sp_ddc_mass_value_changed, 1, 0);
5063         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5064         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5065         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5066         }
5069         /* Trace Background button */
5070         {
5071             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5072                                                           _("Trace Background"),
5073                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5074                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5075                                                           Inkscape::ICON_SIZE_DECORATION );
5076             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5077             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5078             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5079             g_object_set_data( holder, "tracebackground", act );
5080         }
5082         /* Use Pressure button */
5083         {
5084             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5085                                                           _("Pressure"),
5086                                                           _("Use the pressure of the input device to alter the width of the pen"),
5087                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5088                                                           Inkscape::ICON_SIZE_DECORATION );
5089             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5090             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5091             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5092             g_object_set_data( holder, "usepressure", act );
5093         }
5095         /* Use Tilt button */
5096         {
5097             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5098                                                           _("Tilt"),
5099                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5100                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5101                                                           Inkscape::ICON_SIZE_DECORATION );
5102             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5103             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5104             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5105             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5106             g_object_set_data( holder, "usetilt", act );
5107         }
5109         /*calligraphic profile */
5110         {
5111             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5112             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5113             ege_select_one_action_set_appearance (act1, "compact");
5114             g_object_set_data (holder, "profile_selector", act1 );
5116             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5118             sp_dcc_build_presets_list (holder);
5120             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5121             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5122         }
5123     }
5127 //########################
5128 //##    Circle / Arc    ##
5129 //########################
5131 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5133     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5134     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5136     if (v1 == 0 && v2 == 0) {
5137         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5138             gtk_action_set_sensitive( ocb, FALSE );
5139             gtk_action_set_sensitive( make_whole, FALSE );
5140         }
5141     } else {
5142         gtk_action_set_sensitive( ocb, TRUE );
5143         gtk_action_set_sensitive( make_whole, TRUE );
5144     }
5147 static void
5148 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5150     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5152     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5153         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5154         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5155     }
5157     // quit if run by the attr_changed listener
5158     if (g_object_get_data( tbl, "freeze" )) {
5159         return;
5160     }
5162     // in turn, prevent listener from responding
5163     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5165     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5167     bool modmade = false;
5168     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5169          items != NULL;
5170          items = items->next)
5171     {
5172         SPItem *item = SP_ITEM(items->data);
5174         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5176             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5177             SPArc *arc = SP_ARC(item);
5179             if (!strcmp(value_name, "start"))
5180                 ge->start = (adj->value * M_PI)/ 180;
5181             else
5182                 ge->end = (adj->value * M_PI)/ 180;
5184             sp_genericellipse_normalize(ge);
5185             ((SPObject *)arc)->updateRepr();
5186             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5188             modmade = true;
5189         }
5190     }
5192     g_free(namespaced_name);
5194     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5196     sp_arctb_sensitivize( tbl, adj->value, other->value );
5198     if (modmade) {
5199         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5200                                    _("Arc: Change start/end"));
5201     }
5203     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5207 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5209     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5212 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5214     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5218 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5220     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5221     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5222         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5223         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5224     }
5226     // quit if run by the attr_changed listener
5227     if (g_object_get_data( tbl, "freeze" )) {
5228         return;
5229     }
5231     // in turn, prevent listener from responding
5232     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5234     bool modmade = false;
5236     if ( ege_select_one_action_get_active(act) != 0 ) {
5237         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5238              items != NULL;
5239              items = items->next)
5240         {
5241             if (SP_IS_ARC((SPItem *) items->data)) {
5242                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5243                 repr->setAttribute("sodipodi:open", "true");
5244                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5245                 modmade = true;
5246             }
5247         }
5248     } else {
5249         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5250              items != NULL;
5251              items = items->next)
5252         {
5253             if (SP_IS_ARC((SPItem *) items->data))    {
5254                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5255                 repr->setAttribute("sodipodi:open", NULL);
5256                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5257                 modmade = true;
5258             }
5259         }
5260     }
5262     if (modmade) {
5263         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5264                                    _("Arc: Change open/closed"));
5265     }
5267     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5270 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5272     GtkAdjustment *adj;
5273     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5274     gtk_adjustment_set_value(adj, 0.0);
5275     gtk_adjustment_value_changed(adj);
5277     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5278     gtk_adjustment_set_value(adj, 0.0);
5279     gtk_adjustment_value_changed(adj);
5281     spinbutton_defocus( GTK_OBJECT(obj) );
5284 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5285                                       gchar const */*old_value*/, gchar const */*new_value*/,
5286                                       bool /*is_interactive*/, gpointer data)
5288     GObject *tbl = G_OBJECT(data);
5290     // quit if run by the _changed callbacks
5291     if (g_object_get_data( tbl, "freeze" )) {
5292         return;
5293     }
5295     // in turn, prevent callbacks from responding
5296     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5298     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5299     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5301     GtkAdjustment *adj1,*adj2;
5302     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5303     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5304     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5305     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5307     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5309     char const *openstr = NULL;
5310     openstr = repr->attribute("sodipodi:open");
5311     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5313     if (openstr) {
5314         ege_select_one_action_set_active( ocb, 1 );
5315     } else {
5316         ege_select_one_action_set_active( ocb, 0 );
5317     }
5319     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5322 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5323     NULL, /* child_added */
5324     NULL, /* child_removed */
5325     arc_tb_event_attr_changed,
5326     NULL, /* content_changed */
5327     NULL  /* order_changed */
5328 };
5331 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5333     int n_selected = 0;
5334     Inkscape::XML::Node *repr = NULL;
5336     purge_repr_listener( tbl, tbl );
5338     for (GSList const *items = selection->itemList();
5339          items != NULL;
5340          items = items->next)
5341     {
5342         if (SP_IS_ARC((SPItem *) items->data)) {
5343             n_selected++;
5344             repr = SP_OBJECT_REPR((SPItem *) items->data);
5345         }
5346     }
5348     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5350     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5351     if (n_selected == 0) {
5352         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5353     } else if (n_selected == 1) {
5354         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5355         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5357         if (repr) {
5358             g_object_set_data( tbl, "repr", repr );
5359             Inkscape::GC::anchor(repr);
5360             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5361             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5362         }
5363     } else {
5364         // FIXME: implement averaging of all parameters for multiple selected
5365         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5366         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5367         sp_arctb_sensitivize( tbl, 1, 0 );
5368     }
5372 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5374     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5376     EgeAdjustmentAction* eact = 0;
5377     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5380     {
5381         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5382         ege_output_action_set_use_markup( act, TRUE );
5383         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5384         g_object_set_data( holder, "mode_action", act );
5385     }
5387     /* Start */
5388     {
5389         eact = create_adjustment_action( "ArcStartAction",
5390                                          _("Start"), _("Start:"),
5391                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5392                                          "/tools/shapes/arc/start", 0.0,
5393                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5394                                          -360.0, 360.0, 1.0, 10.0,
5395                                          0, 0, 0,
5396                                          sp_arctb_start_value_changed);
5397         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5398     }
5400     /* End */
5401     {
5402         eact = create_adjustment_action( "ArcEndAction",
5403                                          _("End"), _("End:"),
5404                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5405                                          "/tools/shapes/arc/end", 0.0,
5406                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5407                                          -360.0, 360.0, 1.0, 10.0,
5408                                          0, 0, 0,
5409                                          sp_arctb_end_value_changed);
5410         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5411     }
5413     /* Segments / Pie checkbox */
5414     {
5415         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5417         GtkTreeIter iter;
5418         gtk_list_store_append( model, &iter );
5419         gtk_list_store_set( model, &iter,
5420                             0, _("Closed arc"),
5421                             1, _("Switch to segment (closed shape with two radii)"),
5422                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5423                             -1 );
5425         gtk_list_store_append( model, &iter );
5426         gtk_list_store_set( model, &iter,
5427                             0, _("Open Arc"),
5428                             1, _("Switch to arc (unclosed shape)"),
5429                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5430                             -1 );
5432         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5433         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5434         g_object_set_data( holder, "open_action", act );
5436         ege_select_one_action_set_appearance( act, "full" );
5437         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5438         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5439         ege_select_one_action_set_icon_column( act, 2 );
5440         ege_select_one_action_set_icon_size( act, secondarySize );
5441         ege_select_one_action_set_tooltip_column( act, 1  );
5443         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5444         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5445         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5446     }
5448     /* Make Whole */
5449     {
5450         InkAction* inky = ink_action_new( "ArcResetAction",
5451                                           _("Make whole"),
5452                                           _("Make the shape a whole ellipse, not arc or segment"),
5453                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5454                                           secondarySize );
5455         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5456         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5457         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5458         g_object_set_data( holder, "make_whole", inky );
5459     }
5461     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5462     // sensitivize make whole and open checkbox
5463     {
5464         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5465         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5466         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5467     }
5470     sigc::connection *connection = new sigc::connection(
5471         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5472         );
5473     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5474     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5480 // toggle button callbacks and updaters
5482 //########################
5483 //##      Dropper       ##
5484 //########################
5486 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5487     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5488     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5489     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5490     if ( set_action ) {
5491         if ( gtk_toggle_action_get_active( act ) ) {
5492             gtk_action_set_sensitive( set_action, TRUE );
5493         } else {
5494             gtk_action_set_sensitive( set_action, FALSE );
5495         }
5496     }
5498     spinbutton_defocus(GTK_OBJECT(tbl));
5501 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5502     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5503     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5504     spinbutton_defocus(GTK_OBJECT(tbl));
5508 /**
5509  * Dropper auxiliary toolbar construction and setup.
5510  *
5511  * TODO: Would like to add swatch of current color.
5512  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5513  *       can drag and drop places. Will provide a nice mixing palette.
5514  */
5515 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5517     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5518     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5520     {
5521         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5522         ege_output_action_set_use_markup( act, TRUE );
5523         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5524     }
5526     {
5527         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5528                                                       _("Pick opacity"),
5529                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5530                                                       NULL,
5531                                                       Inkscape::ICON_SIZE_DECORATION );
5532         g_object_set( act, "short_label", _("Pick"), NULL );
5533         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5534         g_object_set_data( holder, "pick_action", act );
5535         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5536         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5537     }
5539     {
5540         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5541                                                       _("Assign opacity"),
5542                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5543                                                       NULL,
5544                                                       Inkscape::ICON_SIZE_DECORATION );
5545         g_object_set( act, "short_label", _("Assign"), NULL );
5546         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5547         g_object_set_data( holder, "set_action", act );
5548         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5549         // make sure it's disabled if we're not picking alpha
5550         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5551         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5552     }
5556 //########################
5557 //##      LPETool       ##
5558 //########################
5560 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5562 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5563 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5565     using namespace Inkscape::LivePathEffect;
5567     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5568     SPEventContext *ec = desktop->event_context;
5569     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5570         return;
5571     }
5573     // only take action if run by the attr_changed listener
5574     if (!g_object_get_data(tbl, "freeze")) {
5575         // in turn, prevent listener from responding
5576         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5578         gint mode = ege_select_one_action_get_active(act);
5579         EffectType type = lpesubtools[mode].type;
5581         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5582         bool success = lpetool_try_construction(lc, type);
5583         if (success) {
5584             // since the construction was already performed, we set the state back to inactive
5585             ege_select_one_action_set_active(act, 0);
5586             mode = 0;
5587         } else {
5588             // switch to the chosen subtool
5589             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5590         }
5592         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5593             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5594             prefs->setInt( "/tools/lpetool/mode", mode );
5595         }
5597         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5598     }
5601 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5603     SPEventContext *ec = selection->desktop()->event_context;
5604     if (!SP_IS_LPETOOL_CONTEXT(ec))
5605         return;
5607     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5610 void
5611 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5613     using namespace Inkscape::LivePathEffect;
5614     SPEventContext *ec = selection->desktop()->event_context;
5615     if (!SP_IS_LPETOOL_CONTEXT(ec))
5616         return;
5617     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5619     lpetool_delete_measuring_items(lc);
5620     lpetool_create_measuring_items(lc, selection);
5622     // activate line segment combo box if a single item with LPELineSegment is selected
5623     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5624     SPItem *item = selection->singleItem();
5625     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5626         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5627         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5628         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5629             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5630             g_object_set_data(tbl, "currentlpe", lpe);
5631             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5632             gtk_action_set_sensitive(w, TRUE);
5633             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5634         } else {
5635             g_object_set_data(tbl, "currentlpe", NULL);
5636             g_object_set_data(tbl, "currentlpeitem", NULL);
5637             gtk_action_set_sensitive(w, FALSE);
5638         }
5639     } else {
5640         g_object_set_data(tbl, "currentlpe", NULL);
5641         g_object_set_data(tbl, "currentlpeitem", NULL);
5642         gtk_action_set_sensitive(w, FALSE);
5643     }
5646 static void
5647 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5648     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5649     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5651     bool show = gtk_toggle_action_get_active( act );
5652     prefs->setBool("/tools/lpetool/show_bbox",  show);
5654     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5655         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5656         lpetool_context_reset_limiting_bbox(lc);
5657     }
5660 static void
5661 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5662     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5663     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5664         return;
5666     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5667     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5668     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5669         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5670         bool show = gtk_toggle_action_get_active( act );
5671         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5672         lpetool_show_measuring_info(lc, show);
5673         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5674     }
5677 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5678     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5679     SPUnit const *unit = tracker->getActiveUnit();
5680     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5681     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5683     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5684     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5685         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5686         lpetool_delete_measuring_items(lc);
5687         lpetool_create_measuring_items(lc);
5688     }
5691 static void
5692 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5693     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5694     Inkscape::Selection *selection = desktop->selection;
5696     Geom::OptRect bbox = selection->bounds();
5698     if (bbox) {
5699         Geom::Point A(bbox->min());
5700         Geom::Point B(bbox->max());
5702         A *= desktop->doc2dt();
5703         B *= desktop->doc2dt();
5705         // TODO: should we provide a way to store points in prefs?
5706         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5707         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5708         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5709         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5710         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5712         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5713     }
5715     gtk_toggle_action_set_active(act, false);
5718 static void
5719 sp_line_segment_build_list(GObject *tbl)
5721     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5723     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5724     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5725     gtk_list_store_clear (model);
5727     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5728     {
5729         GtkTreeIter iter;
5730         gtk_list_store_append( model, &iter );
5731         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5732         gtk_list_store_append( model, &iter );
5733         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5734         gtk_list_store_append( model, &iter );
5735         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5736         gtk_list_store_append( model, &iter );
5737         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5738     }
5740     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5743 static void
5744 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5745     using namespace Inkscape::LivePathEffect;
5747     // quit if run by the attr_changed listener
5748     if (g_object_get_data(tbl, "freeze")) {
5749         return;
5750     }
5752     // in turn, prevent listener from responding
5753     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5755     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5756     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5757     if (lpeitem) {
5758         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5759         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5760         sp_lpe_item_update_patheffect(lpeitem, true, true);
5761     }
5763     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5766 static void
5767 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5768     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5770     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5771         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5772     }
5773     gtk_toggle_action_set_active(act, false);
5776 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5778     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5779     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5780     g_object_set_data(holder, "tracker", tracker);
5781     SPUnit const *unit = tracker->getActiveUnit();
5783     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5784     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5786     /** Automatically create a list of LPEs that get added to the toolbar **/
5787     {
5788         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5790         GtkTreeIter iter;
5792         // the first toggle button represents the state that no subtool is active (remove this when
5793         // this can be modeled by EgeSelectOneAction or some other action)
5794         gtk_list_store_append( model, &iter );
5795         gtk_list_store_set( model, &iter,
5796                             0, _("All inactive"),
5797                             1, _("No geometric tool is active"),
5798                             2, "draw-geometry-inactive",
5799                             -1 );
5801         Inkscape::LivePathEffect::EffectType type;
5802         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5803             type =  lpesubtools[i].type;
5804             gtk_list_store_append( model, &iter );
5805             gtk_list_store_set( model, &iter,
5806                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5807                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5808                                 2, lpesubtools[i].icon_name,
5809                                 -1 );
5810         }
5812         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5813         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5814         g_object_set_data( holder, "lpetool_mode_action", act );
5816         ege_select_one_action_set_appearance( act, "full" );
5817         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5818         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5819         ege_select_one_action_set_icon_column( act, 2 );
5820         ege_select_one_action_set_tooltip_column( act, 1  );
5822         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5823         ege_select_one_action_set_active( act, lpeToolMode );
5824         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5825     }
5827     /* Show limiting bounding box */
5828     {
5829         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5830                                                       _("Show limiting bounding box"),
5831                                                       _("Show bounding box (used to cut infinite lines)"),
5832                                                       "show-bounding-box",
5833                                                       Inkscape::ICON_SIZE_DECORATION );
5834         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5835         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5836         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5837     }
5839     /* Set limiting bounding box to bbox of current selection */
5840     {
5841         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5842                                                       _("Get limiting bounding box from selection"),
5843                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5844                                                       "draw-geometry-set-bounding-box",
5845                                                       Inkscape::ICON_SIZE_DECORATION );
5846         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5847         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5848         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5849     }
5852     /* Combo box to choose line segment type */
5853     {
5854         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5855         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5856         ege_select_one_action_set_appearance (act, "compact");
5857         g_object_set_data (holder, "lpetool_line_segment_action", act );
5859         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5861         sp_line_segment_build_list (holder);
5863         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5864         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5865         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5866     }
5868     /* Display measuring info for selected items */
5869     {
5870         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5871                                                       _("Display measuring info"),
5872                                                       _("Display measuring info for selected items"),
5873                                                       "draw-geometry-show-measuring-info",
5874                                                       Inkscape::ICON_SIZE_DECORATION );
5875         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5876         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5877         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5878     }
5880     // add the units menu
5881     {
5882         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5883         gtk_action_group_add_action( mainActions, act );
5884         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5885         g_object_set_data(holder, "lpetool_units_action", act);
5886         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5887     }
5889     /* Open LPE dialog (to adapt parameters numerically) */
5890     {
5891         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5892                                                       _("Open LPE dialog"),
5893                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5894                                                       "dialog-geometry",
5895                                                       Inkscape::ICON_SIZE_DECORATION );
5896         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5897         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5898         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5899     }
5901     //watch selection
5902     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5904     sigc::connection *c_selection_modified =
5905         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5906                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5907     pool->add_connection ("selection-modified", c_selection_modified);
5909     sigc::connection *c_selection_changed =
5910         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5911                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5912     pool->add_connection ("selection-changed", c_selection_changed);
5915 //########################
5916 //##       Eraser       ##
5917 //########################
5919 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5921     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5922     prefs->setDouble( "/tools/eraser/width", adj->value );
5923     update_presets_list(tbl);
5926 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5928     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5929     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5930     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5931         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5932         prefs->setBool( "/tools/eraser/mode", eraserMode );
5933     }
5935     // only take action if run by the attr_changed listener
5936     if (!g_object_get_data( tbl, "freeze" )) {
5937         // in turn, prevent listener from responding
5938         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5940         if ( eraserMode != 0 ) {
5941         } else {
5942         }
5943         // TODO finish implementation
5945         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5946     }
5949 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5951     {
5952         /* Width */
5953         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5954         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5955         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5956                                                               _("Pen Width"), _("Width:"),
5957                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5958                                                               "/tools/eraser/width", 15,
5959                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5960                                                               1, 100, 1.0, 10.0,
5961                                                               labels, values, G_N_ELEMENTS(labels),
5962                                                               sp_erc_width_value_changed, 1, 0);
5963         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5964         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5965         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5966     }
5968     {
5969         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5971         GtkTreeIter iter;
5972         gtk_list_store_append( model, &iter );
5973         gtk_list_store_set( model, &iter,
5974                             0, _("Delete"),
5975                             1, _("Delete objects touched by the eraser"),
5976                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5977                             -1 );
5979         gtk_list_store_append( model, &iter );
5980         gtk_list_store_set( model, &iter,
5981                             0, _("Cut"),
5982                             1, _("Cut out from objects"),
5983                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5984                             -1 );
5986         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5987         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5988         g_object_set_data( holder, "eraser_mode_action", act );
5990         ege_select_one_action_set_appearance( act, "full" );
5991         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5992         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5993         ege_select_one_action_set_icon_column( act, 2 );
5994         ege_select_one_action_set_tooltip_column( act, 1  );
5996         /// @todo Convert to boolean?
5997         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5998         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5999         ege_select_one_action_set_active( act, eraserMode );
6000         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6001     }
6005 //########################
6006 //##    Text Toolbox    ##
6007 //########################
6008 /*
6009 static void
6010 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6012     //Call back for letter sizing spinbutton
6015 static void
6016 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6018     //Call back for line height spinbutton
6021 static void
6022 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6024     //Call back for horizontal kerning spinbutton
6027 static void
6028 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6030     //Call back for vertical kerning spinbutton
6033 static void
6034 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6036     //Call back for letter rotation spinbutton
6037 }*/
6039 namespace {
6041 void
6042 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6044     // quit if run by the _changed callbacks
6045     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6046         return;
6047     }
6049     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6051     SPStyle *query =
6052         sp_style_new (SP_ACTIVE_DOCUMENT);
6054     int result_family =
6055         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6057     int result_style =
6058         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6060     int result_numbers =
6061         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6063     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6065     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6066     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6067         // there are no texts in selection, read from prefs
6069             sp_style_read_from_prefs(query, "/tools/text");
6071             if (g_object_get_data(tbl, "text_style_from_prefs")) {
6072             // do not reset the toolbar style from prefs if we already did it last time
6073             sp_style_unref(query);
6074             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6075             return;
6076         }
6077         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6078     } else {
6079         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6080     }
6082     if (query->text)
6083     {
6084         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6085             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6086             gtk_entry_set_text (GTK_ENTRY (entry), "");
6088         } else if (query->text->font_specification.value || query->text->font_family.value) {
6090             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6091             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6093             // Get the font that corresponds
6094             Glib::ustring familyName;
6096             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6097             if (font) {
6098                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6099                 font->Unref();
6100                 font = NULL;
6101             }
6103             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6105             Gtk::TreeIter iter;
6106             try {
6107                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6108                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6109                 iter = model->get_iter(path);
6110             } catch (...) {
6111                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6112                 sp_style_unref(query);
6113                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6114                 return;
6115             }
6117             combo->set_active (iter);
6118         }
6120         //Size
6121         {
6122             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6123             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6124             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6125             g_free(str);
6126         }
6128         //Anchor
6129         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6130         {
6131             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6132             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6133             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6134             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6135         }
6136         else
6137         {
6138             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6139             {
6140                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6141                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6142                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6143                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6144             }
6145             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6146             {
6147                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6148                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6149                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6150                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6151             }
6152             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6153             {
6154                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6155                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6156                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6157                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6158             }
6159         }
6161         //Style
6162         {
6163             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6165             gboolean active = gtk_toggle_button_get_active (button);
6166             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));
6168             if (active != check)
6169             {
6170                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6171                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6172                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6173             }
6174         }
6176         {
6177             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6179             gboolean active = gtk_toggle_button_get_active (button);
6180             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6182             if (active != check)
6183             {
6184                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6185                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6186                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6187             }
6188         }
6190         //Orientation
6191         //locking both buttons, changing one affect all group (both)
6192         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6193         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6195         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6196         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6198         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6199         {
6200             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6201         }
6202         else
6203         {
6204             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6205         }
6206         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6207         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6208     }
6210     sp_style_unref(query);
6212     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6215 void
6216 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6218     sp_text_toolbox_selection_changed (selection, tbl);
6221 void
6222 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6224     sp_text_toolbox_selection_changed (NULL, tbl);
6227 void
6228 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6229                                 GObject             *tbl)
6231     // quit if run by the _changed callbacks
6232     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6233         return;
6234     }
6236     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6238     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6239     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6240     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6242     //g_print ("family changed to: %s\n", family);
6244     SPStyle *query =
6245         sp_style_new (SP_ACTIVE_DOCUMENT);
6247     int result_fontspec =
6248         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6250     SPCSSAttr *css = sp_repr_css_attr_new ();
6252     // First try to get the font spec from the stored value
6253     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6255     if (fontSpec.empty()) {
6256         // Construct a new font specification if it does not yet exist
6257         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6258         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6259         fontFromStyle->Unref();
6260     }
6262     if (!fontSpec.empty()) {
6264         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6266         if (!newFontSpec.empty()) {
6268             if (fontSpec != newFontSpec) {
6270                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6272                 if (font) {
6273                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6275                     // Set all the these just in case they were altered when finding the best
6276                     // match for the new family and old style...
6278                     gchar c[256];
6280                     font->Family(c, 256);
6282                     sp_repr_css_set_property (css, "font-family", c);
6284                     font->Attribute( "weight", c, 256);
6285                     sp_repr_css_set_property (css, "font-weight", c);
6287                     font->Attribute("style", c, 256);
6288                     sp_repr_css_set_property (css, "font-style", c);
6290                     font->Attribute("stretch", c, 256);
6291                     sp_repr_css_set_property (css, "font-stretch", c);
6293                     font->Attribute("variant", c, 256);
6294                     sp_repr_css_set_property (css, "font-variant", c);
6296                     font->Unref();
6297                 }
6298             }
6300         } else {
6301             // If the old font on selection (or default) was not existing on the system,
6302             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6303             // setting the family reported by the family chooser.
6305             //g_print ("fallback setting family: %s\n", family);
6306             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6307             sp_repr_css_set_property (css, "font-family", family);
6308         }
6309     }
6311     // If querying returned nothing, set the default style of the tool (for new texts)
6312     if (result_fontspec == QUERY_STYLE_NOTHING)
6313     {
6314         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6315         prefs->mergeStyle("/tools/text/style", css);
6316         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6317     }
6318     else
6319     {
6320         sp_desktop_set_style (desktop, css, true, true);
6321     }
6323     sp_style_unref(query);
6325     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6326                                    _("Text: Change font family"));
6327     sp_repr_css_attr_unref (css);
6329     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6331     // unfreeze
6332     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6334     // focus to canvas
6335     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6339 void
6340 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6341                                    gpointer          data)
6343     if (g_object_get_data (G_OBJECT (button), "block")) return;
6344     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6345     int prop = GPOINTER_TO_INT(data);
6347     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6348     SPCSSAttr *css = sp_repr_css_attr_new ();
6350     switch (prop)
6351     {
6352         case 0:
6353         {
6354             sp_repr_css_set_property (css, "text-anchor", "start");
6355             sp_repr_css_set_property (css, "text-align", "start");
6356             break;
6357         }
6358         case 1:
6359         {
6360             sp_repr_css_set_property (css, "text-anchor", "middle");
6361             sp_repr_css_set_property (css, "text-align", "center");
6362             break;
6363         }
6365         case 2:
6366         {
6367             sp_repr_css_set_property (css, "text-anchor", "end");
6368             sp_repr_css_set_property (css, "text-align", "end");
6369             break;
6370         }
6372         case 3:
6373         {
6374             sp_repr_css_set_property (css, "text-anchor", "start");
6375             sp_repr_css_set_property (css, "text-align", "justify");
6376             break;
6377         }
6378     }
6380     SPStyle *query =
6381         sp_style_new (SP_ACTIVE_DOCUMENT);
6382     int result_numbers =
6383         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6385     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6386     if (result_numbers == QUERY_STYLE_NOTHING)
6387     {
6388         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6389         prefs->mergeStyle("/tools/text/style", css);
6390     }
6392     sp_style_unref(query);
6394     sp_desktop_set_style (desktop, css, true, true);
6395     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6396                                    _("Text: Change alignment"));
6397     sp_repr_css_attr_unref (css);
6399     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6402 void
6403 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6404                                gpointer          data)
6406     if (g_object_get_data (G_OBJECT (button), "block")) return;
6408     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6409     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6410     int          prop       = GPOINTER_TO_INT(data);
6411     bool         active     = gtk_toggle_button_get_active (button);
6413     SPStyle *query =
6414         sp_style_new (SP_ACTIVE_DOCUMENT);
6416     int result_fontspec =
6417         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6419     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6420     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6421     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6423     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6424     Glib::ustring newFontSpec = "";
6426     if (fontSpec.empty()) {
6427         // Construct a new font specification if it does not yet exist
6428         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6429         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6430         fontFromStyle->Unref();
6431     }
6433     bool nochange = true;
6434     switch (prop)
6435     {
6436         case 0:
6437         {
6438             if (!fontSpec.empty()) {
6439                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6440                 if (!newFontSpec.empty()) {
6441                     // Don't even set the bold if the font didn't exist on the system
6442                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6443                     nochange = false;
6444                 }
6445             }
6446             // set or reset the button according
6447             if(nochange) {
6448                 gboolean check = gtk_toggle_button_get_active (button);
6450                 if (active != check)
6451                 {
6452                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6453                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6454                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6455                 }
6456             }
6458             break;
6459         }
6461         case 1:
6462         {
6463             if (!fontSpec.empty()) {
6464                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6465                 if (!newFontSpec.empty()) {
6466                     // Don't even set the italic if the font didn't exist on the system
6467                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6468                     nochange = false;
6469                 }
6470             }
6471             if(nochange) {
6472                 gboolean check = gtk_toggle_button_get_active (button);
6474                 if (active != check)
6475                 {
6476                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6477                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6478                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6479                 }
6480             }
6481             break;
6482         }
6483     }
6485     if (!newFontSpec.empty()) {
6486         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6487     }
6489     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6490     if (result_fontspec == 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 font style"));
6501     sp_repr_css_attr_unref (css);
6503     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6506 void
6507 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6508                                      gpointer         data)
6510     if (g_object_get_data (G_OBJECT (button), "block")) {
6511         return;
6512     }
6514     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6515     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6516     int          prop       = GPOINTER_TO_INT(data);
6518     switch (prop)
6519     {
6520         case 0:
6521         {
6522             sp_repr_css_set_property (css, "writing-mode", "lr");
6523             break;
6524         }
6526         case 1:
6527         {
6528             sp_repr_css_set_property (css, "writing-mode", "tb");
6529             break;
6530         }
6531     }
6533     SPStyle *query =
6534         sp_style_new (SP_ACTIVE_DOCUMENT);
6535     int result_numbers =
6536         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6538     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6539     if (result_numbers == QUERY_STYLE_NOTHING)
6540     {
6541         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6542         prefs->mergeStyle("/tools/text/style", css);
6543     }
6545     sp_desktop_set_style (desktop, css, true, true);
6546     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6547                                    _("Text: Change orientation"));
6548     sp_repr_css_attr_unref (css);
6550     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6553 gboolean
6554 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6556     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6557     if (!desktop) return FALSE;
6559     switch (get_group0_keyval (event)) {
6560         case GDK_KP_Enter: // chosen
6561         case GDK_Return:
6562             // unfreeze and update, which will defocus
6563             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6564             sp_text_toolbox_family_changed (NULL, tbl);
6565             return TRUE; // I consumed the event
6566             break;
6567         case GDK_Escape:
6568             // defocus
6569             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6570             return TRUE; // I consumed the event
6571             break;
6572     }
6573     return FALSE;
6576 gboolean
6577 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6579     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6580     if (!desktop) return FALSE;
6582     switch (get_group0_keyval (event)) {
6583         case GDK_KP_Enter:
6584         case GDK_Return:
6585         case GDK_Escape: // defocus
6586             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6587             return TRUE; // I consumed the event
6588             break;
6589         case GDK_w:
6590         case GDK_W:
6591             if (event->state & GDK_CONTROL_MASK) {
6592                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6593                 return TRUE; // I consumed the event
6594             }
6595             break;
6596     }
6597     return FALSE;
6601 void
6602 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6603                                GObject     *tbl)
6605      // quit if run by the _changed callbacks
6606     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6607         return;
6608     }
6610     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6612    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6614     // If this is not from selecting a size in the list (in which case get_active will give the
6615     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6616     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6617     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6618    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6619         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6620         return;
6621    }
6623     gdouble value = -1;
6624     {
6625         gchar *endptr;
6626         gchar *const text = gtk_combo_box_get_active_text(cbox);
6627         if (text) {
6628             value = g_strtod(text, &endptr);
6629             if (endptr == text) {  // Conversion failed, non-numeric input.
6630                 value = -1;
6631             }
6632             g_free(text);
6633         }
6634     }
6635     if (value <= 0) {
6636         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6637         return; // could not parse value
6638     }
6640     SPCSSAttr *css = sp_repr_css_attr_new ();
6641     Inkscape::CSSOStringStream osfs;
6642     osfs << value;
6643     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6645     SPStyle *query =
6646         sp_style_new (SP_ACTIVE_DOCUMENT);
6647     int result_numbers =
6648         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6650     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6651     if (result_numbers == QUERY_STYLE_NOTHING)
6652     {
6653         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6654         prefs->mergeStyle("/tools/text/style", css);
6655     }
6657     sp_style_unref(query);
6659     sp_desktop_set_style (desktop, css, true, true);
6660     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6661                                    _("Text: Change font size"));
6662     sp_repr_css_attr_unref (css);
6664     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6666     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6669 gboolean
6670 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6672     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6673     if (!desktop) return FALSE;
6675     if (!g_object_get_data (tbl, "esc-pressed")) {
6676         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6677         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6678         sp_text_toolbox_size_changed (cbox, tbl);
6679         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6680     }
6681     return FALSE; // I consumed the event
6685 gboolean
6686 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6688     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6689     if (!desktop) return FALSE;
6691     switch (get_group0_keyval (event)) {
6692         case GDK_Escape: // defocus
6693             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6694             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6695             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6696             return TRUE; // I consumed the event
6697             break;
6698         case GDK_Return: // defocus
6699         case GDK_KP_Enter:
6700             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6701             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6702             sp_text_toolbox_size_changed (cbox, tbl);
6703             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6704             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6705             return TRUE; // I consumed the event
6706             break;
6707     }
6708     return FALSE;
6711 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6712 // does not work!
6713 gboolean
6714 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6715                                  GdkEventFocus    */*event*/,
6716                                  GObject          *tbl)
6718     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6719     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6720     return FALSE;
6723 gboolean
6724 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6725                                  GdkEventFocus    */*event*/,
6726                                  GObject          *tbl)
6728     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6729     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6730     return FALSE;
6733 void
6734 cell_data_func  (GtkCellLayout */*cell_layout*/,
6735                  GtkCellRenderer   *cell,
6736                  GtkTreeModel      *tree_model,
6737                  GtkTreeIter       *iter,
6738                  gpointer           /*data*/)
6740     gchar *family;
6741     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6742     gchar *const family_escaped = g_markup_escape_text(family, -1);
6744     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6745     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6746     if (show_sample) {
6748         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6749         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6751     std::stringstream markup;
6752     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6753            << family_escaped << "'>" << sample_escaped << "</span>";
6754     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6756         g_free(sample_escaped);
6757     } else {
6758         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6759     }
6761     g_free(family);
6762     g_free(family_escaped);
6765 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6766                                                 GtkTreeModel       *model,
6767                                                 GtkTreeIter        *iter,
6768                                                 GObject            *tbl)
6770     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6771     // press Enter again after choosing a completion)
6772     gchar *family = 0;
6773     gtk_tree_model_get(model, iter, 0, &family, -1);
6775     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6776     gtk_entry_set_text (GTK_ENTRY (entry), family);
6778     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6779     sp_text_toolbox_family_changed (NULL, tbl);
6780     return TRUE;
6784 static void
6785 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6786     GtkEntry *entry;
6787     GtkEntryCompletion *completion;
6788     GtkTreeModel *model;
6790     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6791     completion = gtk_entry_completion_new();
6792     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6793     gtk_entry_completion_set_model(completion, model);
6794     gtk_entry_completion_set_text_column(completion, 0);
6795     gtk_entry_completion_set_inline_completion(completion, FALSE);
6796     gtk_entry_completion_set_inline_selection(completion, FALSE);
6797     gtk_entry_completion_set_popup_completion(completion, TRUE);
6798     gtk_entry_set_completion(entry, completion);
6800     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6802     g_object_unref(completion);
6805 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6806                                       void */*property*/,
6807                                       GObject *tbl)
6809   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6811   gboolean shown;
6812   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6813   if (shown) {
6814          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6815          //g_print("POP: notify: SHOWN\n");
6816   } else {
6817          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6819          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6820          // find out if the drop down was closed by Enter and if so, manually update (only
6821          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6822          GdkEvent *ev = gtk_get_current_event();
6823          if (ev) {
6824              //g_print ("ev type: %d\n", ev->type);
6825              if (ev->type == GDK_KEY_PRESS) {
6826                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6827                      case GDK_KP_Enter: // chosen
6828                      case GDK_Return:
6829                      {
6830                          // make sure the chosen one is inserted into the entry
6831                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6832                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6833                          GtkTreeIter iter;
6834                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6835                          if (has_active) {
6836                              gchar *family;
6837                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6838                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6839                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6840                          }
6842                          // update
6843                          sp_text_toolbox_family_changed (NULL, tbl);
6844                          break;
6845                      }
6846                  }
6847              }
6848          }
6850          // regardless of whether we updated, defocus the widget
6851          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6852          if (desktop)
6853              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6854          //g_print("POP: notify: HIDDEN\n");
6855   }
6858 GtkWidget*
6859 sp_text_toolbox_new (SPDesktop *desktop)
6861     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6862     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6864     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6865     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6867     GtkTooltips *tt = gtk_tooltips_new();
6869     ////////////Family
6870     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6871     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6873     gtk_rc_parse_string (
6874        "style \"dropdown-as-list-style\"\n"
6875        "{\n"
6876        "    GtkComboBox::appears-as-list = 1\n"
6877        "}\n"
6878        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6879     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6880     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6882     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6884     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6886     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6887     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6889     // expand the field a bit so as to view more of the previews in the drop-down
6890     GtkRequisition req;
6891     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6892     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6894     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6895     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6897     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6898     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6899              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6900     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6901     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6902     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6904     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6905     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6907     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6908     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6909     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6910     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6912     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6913     GtkWidget *box = gtk_event_box_new ();
6914     gtk_container_add (GTK_CONTAINER (box), image);
6915     gtk_toolbar_append_widget( tbl, box, "", "");
6916     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6917     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6918     gtk_widget_hide (GTK_WIDGET (box));
6919     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6921     ////////////Size
6922     gchar const *const sizes[] = {
6923         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6924         "16", "18", "20", "22", "24", "28",
6925         "32", "36", "40", "48", "56", "64", "72", "144"
6926     };
6928     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6929     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6930         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6931     }
6932     gtk_widget_set_size_request (cbox, 80, -1);
6933     gtk_toolbar_append_widget( tbl, cbox, "", "");
6934     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6935     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6936     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6937     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6939     ////////////Text anchor
6940     GtkWidget *group   = gtk_radio_button_new (NULL);
6941     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6942     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6944     // left
6945     GtkWidget *rbutton = group;
6946     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6947     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6948     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6950     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6951     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6952     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6953     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6955     // center
6956     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6957     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6958     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6959     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6961     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6962     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6963     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6964     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6966     // right
6967     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6968     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6969     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6970     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6972     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6973     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6974     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6975     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6977     // fill
6978     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6979     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6980     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6981     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6983     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6984     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6985     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6986     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6988     gtk_toolbar_append_widget( tbl, row, "", "");
6990     //spacer
6991     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6993     ////////////Text style
6994     row = gtk_hbox_new (FALSE, 4);
6996     // bold
6997     rbutton = gtk_toggle_button_new ();
6998     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6999     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7000     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7001     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7003     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7004     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7005     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7007     // italic
7008     rbutton = gtk_toggle_button_new ();
7009     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7010     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7011     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7012     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7014     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7015     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7016     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7018     gtk_toolbar_append_widget( tbl, row, "", "");
7020     //spacer
7021     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7023     // Text orientation
7024     group   = gtk_radio_button_new (NULL);
7025     row     = gtk_hbox_new (FALSE, 4);
7026     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7028     // horizontal
7029     rbutton = group;
7030     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7031     gtk_container_add           (GTK_CONTAINER (rbutton),
7032                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7033     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7034     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7036     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7037     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7038     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7040     // vertical
7041     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7042     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7043     gtk_container_add           (GTK_CONTAINER (rbutton),
7044                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7045     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7046     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7048     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7049     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7050     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7051     gtk_toolbar_append_widget( tbl, row, "", "" );
7054     //watch selection
7055     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7057     sigc::connection *c_selection_changed =
7058         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7059                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7060     pool->add_connection ("selection-changed", c_selection_changed);
7062     sigc::connection *c_selection_modified =
7063         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7064                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7065     pool->add_connection ("selection-modified", c_selection_modified);
7067     sigc::connection *c_subselection_changed =
7068         new sigc::connection (desktop->connectToolSubselectionChanged
7069                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7070     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7072     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7075     gtk_widget_show_all( GTK_WIDGET(tbl) );
7077     return GTK_WIDGET(tbl);
7078 } // end of sp_text_toolbox_new()
7080 }//<unnamed> namespace
7083 //#########################
7084 //##      Connector      ##
7085 //#########################
7087 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7089     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7090     prefs->setBool("/tools/connector/mode",
7091                 gtk_toggle_action_get_active( act ));
7094 static void sp_connector_path_set_avoid(void)
7096     cc_selection_set_avoid(true);
7100 static void sp_connector_path_set_ignore(void)
7102     cc_selection_set_avoid(false);
7105 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7107     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7108     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7109     SPDocument *doc = sp_desktop_document(desktop);
7111     if (!sp_document_get_undo_sensitive(doc))
7112     {
7113         return;
7114     }
7117     // quit if run by the _changed callbacks
7118     if (g_object_get_data( tbl, "freeze" )) {
7119         return;
7120     }
7122     // in turn, prevent callbacks from responding
7123     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7125     bool is_orthog = gtk_toggle_action_get_active( act );
7126     gchar orthog_str[] = "orthogonal";
7127     gchar polyline_str[] = "polyline";
7128     gchar *value = is_orthog ? orthog_str : polyline_str ;
7130     bool modmade = false;
7131     GSList *l = (GSList *) selection->itemList();
7132     while (l) {
7133         SPItem *item = (SPItem *) l->data;
7135         if (cc_item_is_connector(item)) {
7136             sp_object_setAttribute(item, "inkscape:connector-type",
7137                     value, false);
7138             item->avoidRef->handleSettingChange();
7139             modmade = true;
7140         }
7141         l = l->next;
7142     }
7144     if (!modmade)
7145     {
7146         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7147         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7148     }
7150     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7151             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7153     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7156 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7158     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7159     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7160     SPDocument *doc = sp_desktop_document(desktop);
7162     if (!sp_document_get_undo_sensitive(doc))
7163     {
7164         return;
7165     }
7168     // quit if run by the _changed callbacks
7169     if (g_object_get_data( tbl, "freeze" )) {
7170         return;
7171     }
7173     // in turn, prevent callbacks from responding
7174     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7176     gdouble newValue = gtk_adjustment_get_value(adj);
7177     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7178     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7180     bool modmade = false;
7181     GSList *l = (GSList *) selection->itemList();
7182     while (l) {
7183         SPItem *item = (SPItem *) l->data;
7185         if (cc_item_is_connector(item)) {
7186             sp_object_setAttribute(item, "inkscape:connector-curvature",
7187                     value, false);
7188             item->avoidRef->handleSettingChange();
7189             modmade = true;
7190         }
7191         l = l->next;
7192     }
7194     if (!modmade)
7195     {
7196         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7197         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7198     }
7200     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7201             _("Change connector curvature"));
7203     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7207 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7209     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7210     SPDocument *doc = sp_desktop_document(desktop);
7212     if (!sp_document_get_undo_sensitive(doc))
7213     {
7214         return;
7215     }
7217     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7219     if ( !repr->attribute("inkscape:connector-spacing") &&
7220             ( adj->value == defaultConnSpacing )) {
7221         // Don't need to update the repr if the attribute doesn't
7222         // exist and it is being set to the default value -- as will
7223         // happen at startup.
7224         return;
7225     }
7227     // quit if run by the attr_changed listener
7228     if (g_object_get_data( tbl, "freeze" )) {
7229         return;
7230     }
7232     // in turn, prevent listener from responding
7233     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7235     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7236     SP_OBJECT(desktop->namedview)->updateRepr();
7238     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7239     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7240         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7241         Geom::Matrix m = Geom::identity();
7242         avoid_item_move(&m, item);
7243     }
7245     if (items) {
7246         g_slist_free(items);
7247     }
7249     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7250             _("Change connector spacing"));
7252     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7255 static void sp_connector_graph_layout(void)
7257     if (!SP_ACTIVE_DESKTOP) return;
7258     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7260     // hack for clones, see comment in align-and-distribute.cpp
7261     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7262     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7264     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7266     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7268     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7271 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7273     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7274     prefs->setBool("/tools/connector/directedlayout",
7275                 gtk_toggle_action_get_active( act ));
7278 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7280     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7281     prefs->setBool("/tools/connector/avoidoverlaplayout",
7282                 gtk_toggle_action_get_active( act ));
7286 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7288     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7289     prefs->setDouble("/tools/connector/length", adj->value);
7292 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7293                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7294                                             bool /*is_interactive*/, gpointer data)
7296     GtkWidget *tbl = GTK_WIDGET(data);
7298     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7299         return;
7300     }
7301     if (strcmp(name, "inkscape:connector-spacing") == 0)
7302     {
7303         GtkAdjustment *adj = (GtkAdjustment*)
7304                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7305         gdouble spacing = defaultConnSpacing;
7306         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7308         gtk_adjustment_set_value(adj, spacing);
7309         gtk_adjustment_value_changed(adj);
7310     }
7312     spinbutton_defocus(GTK_OBJECT(tbl));
7315 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7317     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7318     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7320     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7321         cc_create_connection_point(cc);
7324 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7326     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7327     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7329     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7330         cc_remove_connection_point(cc);
7333 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7334     NULL, /* child_added */
7335     NULL, /* child_removed */
7336     connector_tb_event_attr_changed,
7337     NULL, /* content_changed */
7338     NULL  /* order_changed */
7339 };
7341 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7343     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7344     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7345     SPItem *item = selection->singleItem();
7346     if (SP_IS_PATH(item))
7347     {
7348         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7349         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7350         gtk_toggle_action_set_active(act, is_orthog);
7351         gtk_adjustment_set_value(adj, curvature);
7352     }
7356 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7358     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7359     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7361     // Editing mode toggle button
7362     {
7363         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7364                                                       _("EditMode"),
7365                                                       _("Switch between connection point editing and connector drawing mode"),
7366                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7367                                                       Inkscape::ICON_SIZE_DECORATION );
7368         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7370         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7371         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7372         g_object_set_data( holder, "mode", act );
7373         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7374     }
7377     {
7378         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7379                                           _("Avoid"),
7380                                           _("Make connectors avoid selected objects"),
7381                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7382                                           secondarySize );
7383         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7384         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7385     }
7387     {
7388         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7389                                           _("Ignore"),
7390                                           _("Make connectors ignore selected objects"),
7391                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7392                                           secondarySize );
7393         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7394         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7395     }
7397     // Orthogonal connectors toggle button
7398     {
7399         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7400                                                       _("Orthogonal"),
7401                                                       _("Make connector orthogonal or polyline"),
7402                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7403                                                       Inkscape::ICON_SIZE_DECORATION );
7404         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7406         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7407         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7408         g_object_set_data( holder, "orthogonal", act );
7409         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7410     }
7412     EgeAdjustmentAction* eact = 0;
7413     // Curvature spinbox
7414     eact = create_adjustment_action( "ConnectorCurvatureAction",
7415                                     _("Connector Curvature"), _("Curvature:"),
7416                                     _("The amount of connectors curvature"),
7417                                     "/tools/connector/curvature", defaultConnCurvature,
7418                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7419                                     0, 100, 1.0, 10.0,
7420                                     0, 0, 0,
7421                                     connector_curvature_changed, 1, 0 );
7422     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7424     // Spacing spinbox
7425     eact = create_adjustment_action( "ConnectorSpacingAction",
7426                                     _("Connector Spacing"), _("Spacing:"),
7427                                     _("The amount of space left around objects by auto-routing connectors"),
7428                                     "/tools/connector/spacing", defaultConnSpacing,
7429                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7430                                     0, 100, 1.0, 10.0,
7431                                     0, 0, 0,
7432                                     connector_spacing_changed, 1, 0 );
7433     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7435     // Graph (connector network) layout
7436     {
7437         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7438                                           _("Graph"),
7439                                           _("Nicely arrange selected connector network"),
7440                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7441                                           secondarySize );
7442         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7443         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7444     }
7446     // Default connector length spinbox
7447     eact = create_adjustment_action( "ConnectorLengthAction",
7448                                      _("Connector Length"), _("Length:"),
7449                                      _("Ideal length for connectors when layout is applied"),
7450                                      "/tools/connector/length", 100,
7451                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7452                                      10, 1000, 10.0, 100.0,
7453                                      0, 0, 0,
7454                                      connector_length_changed, 1, 0 );
7455     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7458     // Directed edges toggle button
7459     {
7460         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7461                                                       _("Downwards"),
7462                                                       _("Make connectors with end-markers (arrows) point downwards"),
7463                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7464                                                       Inkscape::ICON_SIZE_DECORATION );
7465         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7467         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7468         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7470         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7471         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7472         );
7473     }
7475     // Avoid overlaps toggle button
7476     {
7477         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7478                                                       _("Remove overlaps"),
7479                                                       _("Do not allow overlapping shapes"),
7480                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7481                                                       Inkscape::ICON_SIZE_DECORATION );
7482         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7484         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7485         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7487         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7488     }
7491     // New connection point button
7492     {
7493         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7494                                           _("New connection point"),
7495                                           _("Add a new connection point to the currently selected item"),
7496                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7497                                           secondarySize );
7498         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7499         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7500     }
7502     // Remove selected connection point button
7504     {
7505         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7506                                           _("Remove connection point"),
7507                                           _("Remove the currently selected connection point"),
7508                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7509                                           secondarySize );
7510         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7511         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7512     }
7515     // Code to watch for changes to the connector-spacing attribute in
7516     // the XML.
7517     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7518     g_assert(repr != NULL);
7520     purge_repr_listener( holder, holder );
7522     if (repr) {
7523         g_object_set_data( holder, "repr", repr );
7524         Inkscape::GC::anchor(repr);
7525         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7526         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7527     }
7528 } // end of sp_connector_toolbox_prep()
7531 //#########################
7532 //##     Paintbucket     ##
7533 //#########################
7535 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7537     gint channels = ege_select_one_action_get_active( act );
7538     flood_channels_set_channels( channels );
7541 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7543     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7544     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7547 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7549     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7550     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7553 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7555     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7556     SPUnit const *unit = tracker->getActiveUnit();
7557     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7559     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7560     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7563 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7565     // FIXME: make defaults settable via Inkscape Options
7566     struct KeyValue {
7567         char const *key;
7568         double value;
7569     } const key_values[] = {
7570         {"threshold", 15},
7571         {"offset", 0.0}
7572     };
7574     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7575         KeyValue const &kv = key_values[i];
7576         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7577         if ( adj ) {
7578             gtk_adjustment_set_value(adj, kv.value);
7579         }
7580     }
7582     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7583     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7584     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7585     ege_select_one_action_set_active( autogap_action, 0 );
7588 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7590     EgeAdjustmentAction* eact = 0;
7591     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7593     {
7594         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7596         GList* items = 0;
7597         gint count = 0;
7598         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7599         {
7600             GtkTreeIter iter;
7601             gtk_list_store_append( model, &iter );
7602             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7603             count++;
7604         }
7605         g_list_free( items );
7606         items = 0;
7607         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7608         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7609         ege_select_one_action_set_appearance( act1, "compact" );
7610         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7611         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7612         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7613         g_object_set_data( holder, "channels_action", act1 );
7614     }
7616     // Spacing spinbox
7617     {
7618         eact = create_adjustment_action(
7619             "ThresholdAction",
7620             _("Fill Threshold"), _("Threshold:"),
7621             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7622             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7623             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7624             0, 0, 0,
7625             paintbucket_threshold_changed, 1, 0 );
7627         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7628         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7629     }
7631     // Create the units menu.
7632     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7633     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7634     if (!stored_unit.empty())
7635         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7636     g_object_set_data( holder, "tracker", tracker );
7637     {
7638         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7639         gtk_action_group_add_action( mainActions, act );
7640     }
7642     // Offset spinbox
7643     {
7644         eact = create_adjustment_action(
7645             "OffsetAction",
7646             _("Grow/shrink by"), _("Grow/shrink by:"),
7647             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7648             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7649             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7650             0, 0, 0,
7651             paintbucket_offset_changed, 1, 2);
7652         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7654         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7655     }
7657     /* Auto Gap */
7658     {
7659         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7661         GList* items = 0;
7662         gint count = 0;
7663         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7664         {
7665             GtkTreeIter iter;
7666             gtk_list_store_append( model, &iter );
7667             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7668             count++;
7669         }
7670         g_list_free( items );
7671         items = 0;
7672         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7673         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7674         ege_select_one_action_set_appearance( act2, "compact" );
7675         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7676         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7677         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7678         g_object_set_data( holder, "autogap_action", act2 );
7679     }
7681     /* Reset */
7682     {
7683         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7684                                           _("Defaults"),
7685                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7686                                           GTK_STOCK_CLEAR );
7687         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7688         gtk_action_group_add_action( mainActions, act );
7689         gtk_action_set_sensitive( act, TRUE );
7690     }
7694 /*
7695   Local Variables:
7696   mode:c++
7697   c-file-style:"stroustrup"
7698   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7699   indent-tabs-mode:nil
7700   fill-column:99
7701   End:
7702 */
7703 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :