Code

9fc0777785a0f3abb7d51bf10570d5bf1a9bcb87
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@users.sf.net>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *
16  * Copyright (C) 2004 David Turner
17  * Copyright (C) 2003 MenTaLguY
18  * Copyright (C) 1999-2008 authors
19  * Copyright (C) 2001-2002 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../node-context.h"
69 #include "../pen-context.h"
70 #include "../preferences.h"
71 #include "../selection-chemistry.h"
72 #include "../selection.h"
73 #include "select-toolbar.h"
74 #include "../shape-editor.h"
75 #include "../shortcuts.h"
76 #include "../sp-clippath.h"
77 #include "../sp-ellipse.h"
78 #include "../sp-flowtext.h"
79 #include "../sp-mask.h"
80 #include "../sp-namedview.h"
81 #include "../sp-rect.h"
82 #include "../sp-spiral.h"
83 #include "../sp-star.h"
84 #include "../sp-text.h"
85 #include "../style.h"
86 #include "../svg/css-ostringstream.h"
87 #include "../tools-switch.h"
88 #include "../tweak-context.h"
89 #include "../spray-context.h"
90 #include "../ui/dialog/calligraphic-profile-rename.h"
91 #include "../ui/icon-names.h"
92 #include "../ui/widget/style-swatch.h"
93 #include "../verbs.h"
94 #include "../widgets/button.h"
95 #include "../widgets/spinbutton-events.h"
96 #include "../widgets/spw-utilities.h"
97 #include "../widgets/widget-sizes.h"
98 #include "../xml/attribute-record.h"
99 #include "../xml/node-event-vector.h"
100 #include "../xml/repr.h"
102 #include "toolbox.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
123 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
131 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
132     static Inkscape::IconSize sizeChoices[] = {
133         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
134         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
135         Inkscape::ICON_SIZE_MENU
136     };
137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
138     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
139     return sizeChoices[index];
142 static struct {
143     gchar const *type_name;
144     gchar const *data_name;
145     sp_verb_t verb;
146     sp_verb_t doubleclick_verb;
147 } const tools[] = {
148     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
149     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
150     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
151     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
152     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
153     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
154     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
155     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
156     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
157     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
158     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
159     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
160     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
161     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
162     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
163     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
164     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
165     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
166     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
167     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
168     { NULL, NULL, 0, 0 }
169 };
171 static struct {
172     gchar const *type_name;
173     gchar const *data_name;
174     GtkWidget *(*create_func)(SPDesktop *desktop);
175     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
176     gchar const *ui_name;
177     gint swatch_verb_id;
178     gchar const *swatch_tool;
179     gchar const *swatch_tip;
180 } const aux_toolboxes[] = {
181     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
182       SP_VERB_INVALID, 0, 0},
183     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
184       SP_VERB_INVALID, 0, 0},
185     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
186       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
187     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
188       SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
189     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
190       SP_VERB_INVALID, 0, 0},
191     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
192       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
193     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
194       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
195     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
196       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
197     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
198       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
199     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
200       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
201     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
202       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
203     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
204       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
205     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
206       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
207     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
208       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
209     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
210       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
211     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
212       SP_VERB_INVALID, 0, 0},
213     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
214       SP_VERB_INVALID, 0, 0},
215     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
216       SP_VERB_INVALID, 0, 0},
217     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
218       SP_VERB_INVALID, 0, 0},
219     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
220       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
221     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
222 };
224 #define TOOLBAR_SLIDER_HINT "full"
226 static gchar const * ui_descr =
227         "<ui>"
228         "  <toolbar name='SelectToolbar'>"
229         "    <toolitem action='EditSelectAll' />"
230         "    <toolitem action='EditSelectAllInAllLayers' />"
231         "    <toolitem action='EditDeselect' />"
232         "    <separator />"
233         "    <toolitem action='ObjectRotate90CCW' />"
234         "    <toolitem action='ObjectRotate90' />"
235         "    <toolitem action='ObjectFlipHorizontally' />"
236         "    <toolitem action='ObjectFlipVertically' />"
237         "    <separator />"
238         "    <toolitem action='SelectionToBack' />"
239         "    <toolitem action='SelectionLower' />"
240         "    <toolitem action='SelectionRaise' />"
241         "    <toolitem action='SelectionToFront' />"
242         "    <separator />"
243         "    <toolitem action='XAction' />"
244         "    <toolitem action='YAction' />"
245         "    <toolitem action='WidthAction' />"
246         "    <toolitem action='LockAction' />"
247         "    <toolitem action='HeightAction' />"
248         "    <toolitem action='UnitsAction' />"
249         "    <separator />"
250         "    <toolitem action='transform_affect_label' />"
251         "    <toolitem action='transform_stroke' />"
252         "    <toolitem action='transform_corners' />"
253         "    <toolitem action='transform_gradient' />"
254         "    <toolitem action='transform_pattern' />"
255         "  </toolbar>"
257         "  <toolbar name='NodeToolbar'>"
258         "    <toolitem action='NodeInsertAction' />"
259         "    <toolitem action='NodeDeleteAction' />"
260         "    <separator />"
261         "    <toolitem action='NodeJoinAction' />"
262         "    <toolitem action='NodeBreakAction' />"
263         "    <separator />"
264         "    <toolitem action='NodeJoinSegmentAction' />"
265         "    <toolitem action='NodeDeleteSegmentAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeCuspAction' />"
268         "    <toolitem action='NodeSmoothAction' />"
269         "    <toolitem action='NodeSymmetricAction' />"
270         "    <toolitem action='NodeAutoAction' />"
271         "    <separator />"
272         "    <toolitem action='NodeLineAction' />"
273         "    <toolitem action='NodeCurveAction' />"
274         "    <separator />"
275         "    <toolitem action='ObjectToPath' />"
276         "    <toolitem action='StrokeToPath' />"
277         "    <separator />"
278         "    <toolitem action='NodeXAction' />"
279         "    <toolitem action='NodeYAction' />"
280         "    <toolitem action='NodeUnitsAction' />"
281         "    <separator />"
282         "    <toolitem action='ObjectEditClipPathAction' />"
283         "    <toolitem action='ObjectEditMaskPathAction' />"
284         "    <toolitem action='EditNextLPEParameterAction' />"
285         "    <separator />"
286         "    <toolitem action='NodesShowHandlesAction' />"
287         "    <toolitem action='NodesShowHelperpath' />"
288         "  </toolbar>"
290         "  <toolbar name='TweakToolbar'>"
291         "    <toolitem action='TweakWidthAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakForceAction' />"
294         "    <toolitem action='TweakPressureAction' />"
295         "    <separator />"
296         "    <toolitem action='TweakModeAction' />"
297         "    <separator />"
298         "    <toolitem action='TweakFidelityAction' />"
299         "    <separator />"
300         "    <toolitem action='TweakChannelsLabel' />"
301         "    <toolitem action='TweakDoH' />"
302         "    <toolitem action='TweakDoS' />"
303         "    <toolitem action='TweakDoL' />"
304         "    <toolitem action='TweakDoO' />"
305         "  </toolbar>"
307         "  <toolbar name='SprayToolbar'>"
308         "    <toolitem action='SprayModeAction' />"
309         "    <separator />"
310         "    <separator />"
311         "    <toolitem action='SprayWidthAction' />"
312         "    <toolitem action='SprayPressureAction' />"
313         "    <toolitem action='SprayPopulationAction' />"
314         "    <separator />"
315         "    <toolitem action='SprayRotationAction' />"
316         "    <toolitem action='SprayScaleAction' />"
317         "    <separator />"
318         "    <toolitem action='SprayStandard_deviationAction' />"
319         "    <toolitem action='SprayMeanAction' />"
320         "  </toolbar>"
322         "  <toolbar name='ZoomToolbar'>"
323         "    <toolitem action='ZoomIn' />"
324         "    <toolitem action='ZoomOut' />"
325         "    <separator />"
326         "    <toolitem action='Zoom1:0' />"
327         "    <toolitem action='Zoom1:2' />"
328         "    <toolitem action='Zoom2:1' />"
329         "    <separator />"
330         "    <toolitem action='ZoomSelection' />"
331         "    <toolitem action='ZoomDrawing' />"
332         "    <toolitem action='ZoomPage' />"
333         "    <toolitem action='ZoomPageWidth' />"
334         "    <separator />"
335         "    <toolitem action='ZoomPrev' />"
336         "    <toolitem action='ZoomNext' />"
337         "  </toolbar>"
339         "  <toolbar name='StarToolbar'>"
340         "    <separator />"
341         "    <toolitem action='StarStateAction' />"
342         "    <separator />"
343         "    <toolitem action='FlatAction' />"
344         "    <separator />"
345         "    <toolitem action='MagnitudeAction' />"
346         "    <toolitem action='SpokeAction' />"
347         "    <toolitem action='RoundednessAction' />"
348         "    <toolitem action='RandomizationAction' />"
349         "    <separator />"
350         "    <toolitem action='StarResetAction' />"
351         "  </toolbar>"
353         "  <toolbar name='RectToolbar'>"
354         "    <toolitem action='RectStateAction' />"
355         "    <toolitem action='RectWidthAction' />"
356         "    <toolitem action='RectHeightAction' />"
357         "    <toolitem action='RadiusXAction' />"
358         "    <toolitem action='RadiusYAction' />"
359         "    <toolitem action='RectUnitsAction' />"
360         "    <separator />"
361         "    <toolitem action='RectResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='3DBoxToolbar'>"
365         "    <toolitem action='3DBoxAngleXAction' />"
366         "    <toolitem action='3DBoxVPXStateAction' />"
367         "    <separator />"
368         "    <toolitem action='3DBoxAngleYAction' />"
369         "    <toolitem action='3DBoxVPYStateAction' />"
370         "    <separator />"
371         "    <toolitem action='3DBoxAngleZAction' />"
372         "    <toolitem action='3DBoxVPZStateAction' />"
373         "  </toolbar>"
375         "  <toolbar name='SpiralToolbar'>"
376         "    <toolitem action='SpiralStateAction' />"
377         "    <toolitem action='SpiralRevolutionAction' />"
378         "    <toolitem action='SpiralExpansionAction' />"
379         "    <toolitem action='SpiralT0Action' />"
380         "    <separator />"
381         "    <toolitem action='SpiralResetAction' />"
382         "  </toolbar>"
384         "  <toolbar name='PenToolbar'>"
385         "    <toolitem action='FreehandModeActionPen' />"
386         "    <separator />"
387         "    <toolitem action='SetPenShapeAction'/>"
388         "  </toolbar>"
390         "  <toolbar name='PencilToolbar'>"
391         "    <toolitem action='FreehandModeActionPencil' />"
392         "    <separator />"
393         "    <toolitem action='PencilToleranceAction' />"
394         "    <separator />"
395         "    <toolitem action='PencilResetAction' />"
396         "    <separator />"
397         "    <toolitem action='SetPencilShapeAction'/>"
398         "  </toolbar>"
400         "  <toolbar name='CalligraphyToolbar'>"
401         "    <separator />"
402         "    <toolitem action='SetProfileAction'/>"
403         "    <separator />"
404         "    <toolitem action='CalligraphyWidthAction' />"
405         "    <toolitem action='PressureAction' />"
406         "    <toolitem action='TraceAction' />"
407         "    <toolitem action='ThinningAction' />"
408         "    <separator />"
409         "    <toolitem action='AngleAction' />"
410         "    <toolitem action='TiltAction' />"
411         "    <toolitem action='FixationAction' />"
412         "    <separator />"
413         "    <toolitem action='CapRoundingAction' />"
414         "    <separator />"
415         "    <toolitem action='TremorAction' />"
416         "    <toolitem action='WiggleAction' />"
417         "    <toolitem action='MassAction' />"
418         "    <separator />"
419         "  </toolbar>"
421         "  <toolbar name='ArcToolbar'>"
422         "    <toolitem action='ArcStateAction' />"
423         "    <separator />"
424         "    <toolitem action='ArcStartAction' />"
425         "    <toolitem action='ArcEndAction' />"
426         "    <separator />"
427         "    <toolitem action='ArcOpenAction' />"
428         "    <separator />"
429         "    <toolitem action='ArcResetAction' />"
430         "    <separator />"
431         "  </toolbar>"
433         "  <toolbar name='PaintbucketToolbar'>"
434         "    <toolitem action='ChannelsAction' />"
435         "    <separator />"
436         "    <toolitem action='ThresholdAction' />"
437         "    <separator />"
438         "    <toolitem action='OffsetAction' />"
439         "    <toolitem action='PaintbucketUnitsAction' />"
440         "    <separator />"
441         "    <toolitem action='AutoGapAction' />"
442         "    <separator />"
443         "    <toolitem action='PaintbucketResetAction' />"
444         "  </toolbar>"
446         "  <toolbar name='EraserToolbar'>"
447         "    <toolitem action='EraserWidthAction' />"
448         "    <separator />"
449         "    <toolitem action='EraserModeAction' />"
450         "  </toolbar>"
452         "  <toolbar name='LPEToolToolbar'>"
453         "    <toolitem action='LPEToolModeAction' />"
454         "    <separator />"
455         "    <toolitem action='LPEShowBBoxAction' />"
456         "    <toolitem action='LPEBBoxFromSelectionAction' />"
457         "    <separator />"
458         "    <toolitem action='LPELineSegmentAction' />"
459         "    <separator />"
460         "    <toolitem action='LPEMeasuringAction' />"
461         "    <toolitem action='LPEToolUnitsAction' />"
462         "    <separator />"
463         "    <toolitem action='LPEOpenLPEDialogAction' />"
464         "  </toolbar>"
466         "  <toolbar name='DropperToolbar'>"
467         "    <toolitem action='DropperOpacityAction' />"
468         "    <toolitem action='DropperPickAlphaAction' />"
469         "    <toolitem action='DropperSetAlphaAction' />"
470         "  </toolbar>"
472         "  <toolbar name='ConnectorToolbar'>"
473         "    <toolitem action='ConnectorEditModeAction' />"
474         "    <toolitem action='ConnectorAvoidAction' />"
475         "    <toolitem action='ConnectorIgnoreAction' />"
476         "    <toolitem action='ConnectorOrthogonalAction' />"
477         "    <toolitem action='ConnectorCurvatureAction' />"
478         "    <toolitem action='ConnectorSpacingAction' />"
479         "    <toolitem action='ConnectorGraphAction' />"
480         "    <toolitem action='ConnectorLengthAction' />"
481         "    <toolitem action='ConnectorDirectedAction' />"
482         "    <toolitem action='ConnectorOverlapAction' />"
483         "    <toolitem action='ConnectorNewConnPointAction' />"
484         "    <toolitem action='ConnectorRemoveConnPointAction' />"
485         "  </toolbar>"
487         "</ui>"
490 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
492 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
494 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
495 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
497 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
498 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
500 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
501 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
503 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
504                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
505                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
507 class VerbAction : public Gtk::Action {
508 public:
509     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
511     virtual ~VerbAction();
512     virtual void set_active(bool active = true);
514 protected:
515     virtual Gtk::Widget* create_menu_item_vfunc();
516     virtual Gtk::Widget* create_tool_item_vfunc();
518     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
519     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
521     virtual void on_activate();
523 private:
524     Inkscape::Verb* verb;
525     Inkscape::Verb* verb2;
526     Inkscape::UI::View::View *view;
527     GtkTooltips *tooltips;
528     bool active;
530     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
531 };
534 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
536     Glib::RefPtr<VerbAction> result;
537     SPAction *action = verb->get_action(view);
538     if ( action ) {
539         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
540         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
541     }
543     return result;
546 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
547     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
548     verb(verb),
549     verb2(verb2),
550     view(view),
551     tooltips(tooltips),
552     active(false)
556 VerbAction::~VerbAction()
560 Gtk::Widget* VerbAction::create_menu_item_vfunc()
562 // First call in to get the icon rendered if present in SVG
563     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
564     delete widget;
565     widget = 0;
567     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
568 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
569     return widg;
572 Gtk::Widget* VerbAction::create_tool_item_vfunc()
574 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
575     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
576     GtkWidget* toolbox = 0;
577     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
578                                                                           SP_BUTTON_TYPE_TOGGLE,
579                                                                           verb,
580                                                                           verb2,
581                                                                           view,
582                                                                           tooltips );
583     if ( active ) {
584         sp_button_toggle_set_down( SP_BUTTON(button), active);
585     }
586     gtk_widget_show_all( button );
587     Gtk::Widget* wrapped = Glib::wrap(button);
588     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
589     holder->add(*wrapped);
591 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
592     return holder;
595 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
597 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
598     Gtk::Action::connect_proxy_vfunc(proxy);
601 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
603 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
604     Gtk::Action::disconnect_proxy_vfunc(proxy);
607 void VerbAction::set_active(bool active)
609     this->active = active;
610     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
611     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
612         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
613         if (ti) {
614             // *should* have one child that is the SPButton
615             Gtk::Widget* child = ti->get_child();
616             if ( child && SP_IS_BUTTON(child->gobj()) ) {
617                 SPButton* button = SP_BUTTON(child->gobj());
618                 sp_button_toggle_set_down( button, active );
619             }
620         }
621     }
624 void VerbAction::on_activate()
626     if ( verb ) {
627         SPAction *action = verb->get_action(view);
628         if ( action ) {
629             sp_action_perform(action, 0);
630         }
631     }
634 /* Global text entry widgets necessary for update */
635 /* GtkWidget *dropper_rgb_entry,
636           *dropper_opacity_entry ; */
637 // should be made a private member once this is converted to class
639 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
640     connection->disconnect();
641     delete connection;
644 static void purge_repr_listener( GObject* obj, GObject* tbl )
646     (void)obj;
647     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
648     if (oldrepr) { // remove old listener
649         sp_repr_remove_listener_by_data(oldrepr, tbl);
650         Inkscape::GC::release(oldrepr);
651         oldrepr = 0;
652         g_object_set_data( tbl, "repr", NULL );
653     }
656 GtkWidget *
657 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
658                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
659                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
661     SPAction *action = verb->get_action(view);
662     if (!action) return NULL;
664     SPAction *doubleclick_action;
665     if (doubleclick_verb)
666         doubleclick_action = doubleclick_verb->get_action(view);
667     else
668         doubleclick_action = NULL;
670     /* fixme: Handle sensitive/unsensitive */
671     /* fixme: Implement sp_button_new_from_action */
672     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
673     gtk_widget_show(b);
676     unsigned int shortcut = sp_shortcut_get_primary(verb);
677     if (shortcut) {
678         gchar key[256];
679         sp_ui_shortcut_string(shortcut, key);
680         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
681         if ( t ) {
682             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
683         }
684         g_free(tip);
685     } else {
686         if ( t ) {
687             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
688         }
689     }
691     return b;
695 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
697     SPAction* targetAction = SP_ACTION(user_data);
698     if ( targetAction ) {
699         sp_action_perform( targetAction, NULL );
700     }
703 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
705     if ( data ) {
706         GtkAction* act = GTK_ACTION(data);
707         gtk_action_set_sensitive( act, sensitive );
708     }
711 static SPActionEventVector action_event_vector = {
712     {NULL},
713     NULL,
714     NULL,
715     sp_action_action_set_sensitive,
716     NULL,
717     NULL
718 };
720 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
722     GtkAction* act = 0;
724     SPAction* targetAction = verb->get_action(view);
725     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
726     act = GTK_ACTION(inky);
727     gtk_action_set_sensitive( act, targetAction->sensitive );
729     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
731     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
732     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
734     return act;
737 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
739     Inkscape::UI::View::View *view = desktop;
740     gint verbsToUse[] = {
741         // disabled until we have icons for them:
742         //find
743         //SP_VERB_EDIT_TILE,
744         //SP_VERB_EDIT_UNTILE,
745         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
746         SP_VERB_DIALOG_DISPLAY,
747         SP_VERB_DIALOG_FILL_STROKE,
748         SP_VERB_DIALOG_NAMEDVIEW,
749         SP_VERB_DIALOG_TEXT,
750         SP_VERB_DIALOG_XML_EDITOR,
751         SP_VERB_DIALOG_LAYERS,
752         SP_VERB_EDIT_CLONE,
753         SP_VERB_EDIT_COPY,
754         SP_VERB_EDIT_CUT,
755         SP_VERB_EDIT_DUPLICATE,
756         SP_VERB_EDIT_PASTE,
757         SP_VERB_EDIT_REDO,
758         SP_VERB_EDIT_UNDO,
759         SP_VERB_EDIT_UNLINK_CLONE,
760         SP_VERB_FILE_EXPORT,
761         SP_VERB_FILE_IMPORT,
762         SP_VERB_FILE_NEW,
763         SP_VERB_FILE_OPEN,
764         SP_VERB_FILE_PRINT,
765         SP_VERB_FILE_SAVE,
766         SP_VERB_OBJECT_TO_CURVE,
767         SP_VERB_SELECTION_GROUP,
768         SP_VERB_SELECTION_OUTLINE,
769         SP_VERB_SELECTION_UNGROUP,
770         SP_VERB_ZOOM_1_1,
771         SP_VERB_ZOOM_1_2,
772         SP_VERB_ZOOM_2_1,
773         SP_VERB_ZOOM_DRAWING,
774         SP_VERB_ZOOM_IN,
775         SP_VERB_ZOOM_NEXT,
776         SP_VERB_ZOOM_OUT,
777         SP_VERB_ZOOM_PAGE,
778         SP_VERB_ZOOM_PAGE_WIDTH,
779         SP_VERB_ZOOM_PREV,
780         SP_VERB_ZOOM_SELECTION,
781     };
783     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
785     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
786     Glib::RefPtr<Gtk::ActionGroup> mainActions;
787     if ( groups.find(desktop) != groups.end() ) {
788         mainActions = groups[desktop];
789     }
791     if ( !mainActions ) {
792         mainActions = Gtk::ActionGroup::create("main");
793         groups[desktop] = mainActions;
794     }
796     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
797         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
798         if ( verb ) {
799             if (!mainActions->get_action(verb->get_id())) {
800                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
801                 mainActions->add(Glib::wrap(act));
802             }
803         }
804     }
806     if ( !mainActions->get_action("ToolZoom") ) {
807         GtkTooltips *tt = gtk_tooltips_new();
808         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
809             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
810             if ( va ) {
811                 mainActions->add(va);
812                 if ( i == 0 ) {
813                     va->set_active(true);
814                 }
815             }
816         }
817     }
820     return mainActions;
824 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
826     gtk_widget_set_size_request( widget,
827                                  widget->allocation.width,
828                                  widget->allocation.height );
831 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
833     gtk_widget_set_size_request( widget, -1, -1 );
838 GtkWidget *
839 sp_tool_toolbox_new()
841     GtkTooltips *tt = gtk_tooltips_new();
842     GtkWidget* tb = gtk_toolbar_new();
843     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
844     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
846     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
847     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
849     gtk_widget_set_sensitive(tb, FALSE);
851     GtkWidget *hb = gtk_handle_box_new();
852     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
853     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
854     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
856     gtk_container_add(GTK_CONTAINER(hb), tb);
857     gtk_widget_show(GTK_WIDGET(tb));
859     sigc::connection* conn = new sigc::connection;
860     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
862     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
863     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
865     return hb;
868 GtkWidget *
869 sp_aux_toolbox_new()
871     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
873     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
875     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
877     gtk_widget_set_sensitive(tb, FALSE);
879     GtkWidget *hb = gtk_handle_box_new();
880     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
881     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
882     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
884     gtk_container_add(GTK_CONTAINER(hb), tb);
885     gtk_widget_show(GTK_WIDGET(tb));
887     sigc::connection* conn = new sigc::connection;
888     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
890     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
891     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
893     return hb;
896 //####################################
897 //# Commands Bar
898 //####################################
900 GtkWidget *
901 sp_commands_toolbox_new()
903     GtkWidget *tb = gtk_toolbar_new();
905     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
906     gtk_widget_set_sensitive(tb, FALSE);
908     GtkWidget *hb = gtk_handle_box_new();
909     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
910     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
911     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
913     gtk_container_add(GTK_CONTAINER(hb), tb);
914     gtk_widget_show(GTK_WIDGET(tb));
916     sigc::connection* conn = new sigc::connection;
917     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
919     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
920     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
922     return hb;
925 GtkWidget *
926 sp_snap_toolbox_new()
928     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
929     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
930     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
932     //GtkWidget *tb = gtk_toolbar_new();
933     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
935     gtk_widget_set_sensitive(tb, FALSE);
937     GtkWidget *hb = gtk_handle_box_new();
938     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
939     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
940     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
942     gtk_container_add(GTK_CONTAINER(hb), tb);
943     gtk_widget_show(GTK_WIDGET(tb));
945     sigc::connection* conn = new sigc::connection;
946     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
948     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
949     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
951     return hb;
954 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
955                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
956                                                        Glib::ustring const &path, gdouble def,
957                                                        GtkWidget *focusTarget,
958                                                        GtkWidget *us,
959                                                        GObject *dataKludge,
960                                                        gboolean altx, gchar const *altx_mark,
961                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
962                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
963                                                        void (*callback)(GtkAdjustment *, GObject *),
964                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
966     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
967     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
968                                                              lower, upper, step, page, 0 ) );
969     if (us) {
970         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
971     }
973     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
975     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
976     if ( shortLabel ) {
977         g_object_set( act, "short_label", shortLabel, NULL );
978     }
980     if ( (descrCount > 0) && descrLabels && descrValues ) {
981         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
982     }
984     if ( focusTarget ) {
985         ege_adjustment_action_set_focuswidget( act, focusTarget );
986     }
988     if ( altx && altx_mark ) {
989         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
990     }
992     if ( dataKludge ) {
993         // Rather lame, but it's the only place where we need to get the entry name
994         // but we don't have an Entry
995         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
996     }
998     // Using a cast just to make sure we pass in the right kind of function pointer
999     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1001     return act;
1005 //####################################
1006 //# node editing callbacks
1007 //####################################
1009 /**
1010  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
1011  */
1012 static ShapeEditor *get_current_shape_editor()
1014     if (!SP_ACTIVE_DESKTOP) {
1015         return NULL;
1016     }
1018     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
1020     if (!SP_IS_NODE_CONTEXT(event_context)) {
1021         return NULL;
1022     }
1024     return event_context->shape_editor;
1028 void
1029 sp_node_path_edit_add(void)
1031     ShapeEditor *shape_editor = get_current_shape_editor();
1032     if (shape_editor) shape_editor->add_node();
1035 void
1036 sp_node_path_edit_delete(void)
1038     ShapeEditor *shape_editor = get_current_shape_editor();
1039     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1042 void
1043 sp_node_path_edit_delete_segment(void)
1045     ShapeEditor *shape_editor = get_current_shape_editor();
1046     if (shape_editor) shape_editor->delete_segment();
1049 void
1050 sp_node_path_edit_break(void)
1052     ShapeEditor *shape_editor = get_current_shape_editor();
1053     if (shape_editor) shape_editor->break_at_nodes();
1056 void
1057 sp_node_path_edit_join(void)
1059     ShapeEditor *shape_editor = get_current_shape_editor();
1060     if (shape_editor) shape_editor->join_nodes();
1063 void
1064 sp_node_path_edit_join_segment(void)
1066     ShapeEditor *shape_editor = get_current_shape_editor();
1067     if (shape_editor) shape_editor->join_segments();
1070 void
1071 sp_node_path_edit_toline(void)
1073     ShapeEditor *shape_editor = get_current_shape_editor();
1074     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1077 void
1078 sp_node_path_edit_tocurve(void)
1080     ShapeEditor *shape_editor = get_current_shape_editor();
1081     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1084 void
1085 sp_node_path_edit_cusp(void)
1087     ShapeEditor *shape_editor = get_current_shape_editor();
1088     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1091 void
1092 sp_node_path_edit_smooth(void)
1094     ShapeEditor *shape_editor = get_current_shape_editor();
1095     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1098 void
1099 sp_node_path_edit_symmetrical(void)
1101     ShapeEditor *shape_editor = get_current_shape_editor();
1102     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1105 void
1106 sp_node_path_edit_auto(void)
1108     ShapeEditor *shape_editor = get_current_shape_editor();
1109     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1112 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1113     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1114     bool show = gtk_toggle_action_get_active( act );
1115     prefs->setBool("/tools/nodes/show_handles",  show);
1116     ShapeEditor *shape_editor = get_current_shape_editor();
1117     if (shape_editor) shape_editor->show_handles(show);
1120 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1121     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1122     bool show = gtk_toggle_action_get_active( act );
1123     prefs->setBool("/tools/nodes/show_helperpath",  show);
1124     ShapeEditor *shape_editor = get_current_shape_editor();
1125     if (shape_editor) shape_editor->show_helperpath(show);
1128 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1129     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1132 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1133     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1136 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1137     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1140 /* is called when the node selection is modified */
1141 static void
1142 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1144     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1145     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1146     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1147     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1149     // quit if run by the attr_changed listener
1150     if (g_object_get_data( tbl, "freeze" )) {
1151         return;
1152     }
1154     // in turn, prevent listener from responding
1155     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1157     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1158     SPUnit const *unit = tracker->getActiveUnit();
1160     ShapeEditor *shape_editor = get_current_shape_editor();
1161     if (shape_editor && shape_editor->has_nodepath()) {
1162         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1163         int n_selected = 0;
1164         if (nodepath) {
1165             n_selected = nodepath->numSelected();
1166         }
1168         if (n_selected == 0) {
1169             gtk_action_set_sensitive(xact, FALSE);
1170             gtk_action_set_sensitive(yact, FALSE);
1171         } else {
1172             gtk_action_set_sensitive(xact, TRUE);
1173             gtk_action_set_sensitive(yact, TRUE);
1174             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1175             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1177             if (n_selected == 1) {
1178                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1179                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1180                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1181                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1182                 }
1183             } else {
1184                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1185                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1186                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1187                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1188                        selected nodes don't coincide (in this case we use the coordinates of the center
1189                        of the bounding box). So the entries are never set to zero. */
1190                     // FIXME: Maybe we should clear the entry if several nodes are selected
1191                     //        instead of providing a kind of average value
1192                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1193                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1194                 }
1195             }
1196         }
1197     } else {
1198         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1199         gtk_action_set_sensitive(xact, FALSE);
1200         gtk_action_set_sensitive(yact, FALSE);
1201     }
1203     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1206 static void
1207 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1209     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1210     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1212     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1213     SPUnit const *unit = tracker->getActiveUnit();
1215     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1216         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1217     }
1219     // quit if run by the attr_changed listener
1220     if (g_object_get_data( tbl, "freeze" )) {
1221         return;
1222     }
1224     // in turn, prevent listener from responding
1225     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1227     ShapeEditor *shape_editor = get_current_shape_editor();
1228     if (shape_editor && shape_editor->has_nodepath()) {
1229         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1230         if (!strcmp(value_name, "x")) {
1231             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1232         }
1233         if (!strcmp(value_name, "y")) {
1234             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1235         }
1236     }
1238     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1241 static void
1242 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1244     sp_node_path_value_changed(adj, tbl, "x");
1247 static void
1248 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1250     sp_node_path_value_changed(adj, tbl, "y");
1253 void
1254 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1256     {
1257     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1258     SPItem *item = selection->singleItem();
1259     if (item && SP_IS_LPE_ITEM(item)) {
1260        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1261            gtk_action_set_sensitive(w, TRUE);
1262        } else {
1263            gtk_action_set_sensitive(w, FALSE);
1264        }
1265     } else {
1266        gtk_action_set_sensitive(w, FALSE);
1267     }
1268     }
1270     {
1271     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1272     SPItem *item = selection->singleItem();
1273     if (item && item->clip_ref && item->clip_ref->getObject()) {
1274        gtk_action_set_sensitive(w, TRUE);
1275     } else {
1276        gtk_action_set_sensitive(w, FALSE);
1277     }
1278     }
1280     {
1281     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1282     SPItem *item = selection->singleItem();
1283     if (item && item->mask_ref && item->mask_ref->getObject()) {
1284        gtk_action_set_sensitive(w, TRUE);
1285     } else {
1286        gtk_action_set_sensitive(w, FALSE);
1287     }
1288     }
1291 void
1292 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1294     sp_node_toolbox_sel_changed (selection, tbl);
1299 //################################
1300 //##    Node Editing Toolbox    ##
1301 //################################
1303 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1305     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1306     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1307     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1308     g_object_set_data( holder, "tracker", tracker );
1310     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1312     {
1313         InkAction* inky = ink_action_new( "NodeInsertAction",
1314                                           _("Insert node"),
1315                                           _("Insert new nodes into selected segments"),
1316                                           INKSCAPE_ICON_NODE_ADD,
1317                                           secondarySize );
1318         g_object_set( inky, "short_label", _("Insert"), NULL );
1319         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1320         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1321     }
1323     {
1324         InkAction* inky = ink_action_new( "NodeDeleteAction",
1325                                           _("Delete node"),
1326                                           _("Delete selected nodes"),
1327                                           INKSCAPE_ICON_NODE_DELETE,
1328                                           secondarySize );
1329         g_object_set( inky, "short_label", _("Delete"), NULL );
1330         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1331         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1332     }
1334     {
1335         InkAction* inky = ink_action_new( "NodeJoinAction",
1336                                           _("Join endnodes"),
1337                                           _("Join selected endnodes"),
1338                                           INKSCAPE_ICON_NODE_JOIN,
1339                                           secondarySize );
1340         g_object_set( inky, "short_label", _("Join"), NULL );
1341         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1342         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1343     }
1345     {
1346         InkAction* inky = ink_action_new( "NodeBreakAction",
1347                                           _("Break nodes"),
1348                                           _("Break path at selected nodes"),
1349                                           INKSCAPE_ICON_NODE_BREAK,
1350                                           secondarySize );
1351         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1352         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1353     }
1356     {
1357         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1358                                           _("Join with segment"),
1359                                           _("Join selected endnodes with a new segment"),
1360                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1361                                           secondarySize );
1362         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1363         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1364     }
1366     {
1367         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1368                                           _("Delete segment"),
1369                                           _("Delete segment between two non-endpoint nodes"),
1370                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1371                                           secondarySize );
1372         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1373         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1374     }
1376     {
1377         InkAction* inky = ink_action_new( "NodeCuspAction",
1378                                           _("Node Cusp"),
1379                                           _("Make selected nodes corner"),
1380                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1381                                           secondarySize );
1382         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1383         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1384     }
1386     {
1387         InkAction* inky = ink_action_new( "NodeSmoothAction",
1388                                           _("Node Smooth"),
1389                                           _("Make selected nodes smooth"),
1390                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1391                                           secondarySize );
1392         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1393         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1394     }
1396     {
1397         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1398                                           _("Node Symmetric"),
1399                                           _("Make selected nodes symmetric"),
1400                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1401                                           secondarySize );
1402         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1403         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1404     }
1406     {
1407         InkAction* inky = ink_action_new( "NodeAutoAction",
1408                                           _("Node Auto"),
1409                                           _("Make selected nodes auto-smooth"),
1410                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1411                                           secondarySize );
1412         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1413         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1414     }
1416     {
1417         InkAction* inky = ink_action_new( "NodeLineAction",
1418                                           _("Node Line"),
1419                                           _("Make selected segments lines"),
1420                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1421                                           secondarySize );
1422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424     }
1426     {
1427         InkAction* inky = ink_action_new( "NodeCurveAction",
1428                                           _("Node Curve"),
1429                                           _("Make selected segments curves"),
1430                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1431                                           secondarySize );
1432         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1433         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1434     }
1436     {
1437         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1438                                                       _("Show Handles"),
1439                                                       _("Show the Bezier handles of selected nodes"),
1440                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1441                                                       Inkscape::ICON_SIZE_DECORATION );
1442         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1443         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1444         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1445     }
1447     {
1448         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1449                                                       _("Show Outline"),
1450                                                       _("Show the outline of the path"),
1451                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1452                                                       Inkscape::ICON_SIZE_DECORATION );
1453         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1454         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1455         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1456     }
1458     {
1459         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1460                                           _("Next path effect parameter"),
1461                                           _("Show next path effect parameter for editing"),
1462                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1463                                           Inkscape::ICON_SIZE_DECORATION );
1464         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1465         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1466         g_object_set_data( holder, "nodes_lpeedit", inky);
1467     }
1469     {
1470         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1471                                           _("Edit clipping path"),
1472                                           _("Edit the clipping path of the object"),
1473                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1474                                           Inkscape::ICON_SIZE_DECORATION );
1475         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1476         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1477         g_object_set_data( holder, "nodes_clippathedit", inky);
1478     }
1480     {
1481         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1482                                           _("Edit mask path"),
1483                                           _("Edit the mask of the object"),
1484                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1485                                           Inkscape::ICON_SIZE_DECORATION );
1486         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1487         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1488         g_object_set_data( holder, "nodes_maskedit", inky);
1489     }
1491     /* X coord of selected node(s) */
1492     {
1493         EgeAdjustmentAction* eact = 0;
1494         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1495         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1496         eact = create_adjustment_action( "NodeXAction",
1497                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1498                                          "/tools/nodes/Xcoord", 0,
1499                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1500                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1501                                          labels, values, G_N_ELEMENTS(labels),
1502                                          sp_node_path_x_value_changed );
1503         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1504         g_object_set_data( holder, "nodes_x_action", eact );
1505         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1506         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1507     }
1509     /* Y coord of selected node(s) */
1510     {
1511         EgeAdjustmentAction* eact = 0;
1512         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1513         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1514         eact = create_adjustment_action( "NodeYAction",
1515                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1516                                          "/tools/nodes/Ycoord", 0,
1517                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1518                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1519                                          labels, values, G_N_ELEMENTS(labels),
1520                                          sp_node_path_y_value_changed );
1521         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1522         g_object_set_data( holder, "nodes_y_action", eact );
1523         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1524         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1525     }
1527     // add the units menu
1528     {
1529         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1530         gtk_action_group_add_action( mainActions, act );
1531     }
1534     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1536     //watch selection
1537     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1539     sigc::connection *c_selection_changed =
1540         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1541                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1542     pool->add_connection ("selection-changed", c_selection_changed);
1544     sigc::connection *c_selection_modified =
1545         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1546                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1547     pool->add_connection ("selection-modified", c_selection_modified);
1549     sigc::connection *c_subselection_changed =
1550         new sigc::connection (desktop->connectToolSubselectionChanged
1551                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1552     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1554     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1556     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1557 } // end of sp_node_toolbox_prep()
1560 //########################
1561 //##    Zoom Toolbox    ##
1562 //########################
1564 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1566     // no custom GtkAction setup needed
1567 } // end of sp_zoom_toolbox_prep()
1569 void
1570 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1572     toolbox_set_desktop(toolbox,
1573                         desktop,
1574                         setup_tool_toolbox,
1575                         update_tool_toolbox,
1576                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1577                                                                          "event_context_connection")));
1581 void
1582 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1584     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1585                         desktop,
1586                         setup_aux_toolbox,
1587                         update_aux_toolbox,
1588                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1589                                                                          "event_context_connection")));
1592 void
1593 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1595     toolbox_set_desktop(toolbox,
1596                         desktop,
1597                         setup_commands_toolbox,
1598                         update_commands_toolbox,
1599                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1600                                                                          "event_context_connection")));
1603 void
1604 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1606     toolbox_set_desktop(toolbox,
1607                         desktop,
1608                         setup_snap_toolbox,
1609                         update_snap_toolbox,
1610                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1611                                                                          "event_context_connection")));
1615 static void
1616 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1618     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1619     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1621     if (old_desktop) {
1622         GList *children, *iter;
1624         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1625         for ( iter = children ; iter ; iter = iter->next ) {
1626             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1627         }
1628         g_list_free(children);
1629     }
1631     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1633     if (desktop) {
1634         gtk_widget_set_sensitive(toolbox, TRUE);
1635         setup_func(toolbox, desktop);
1636         update_func(desktop, desktop->event_context, toolbox);
1637         *conn = desktop->connectEventContextChanged
1638             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1639     } else {
1640         gtk_widget_set_sensitive(toolbox, FALSE);
1641     }
1643 } // end of toolbox_set_desktop()
1646 static void
1647 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1649     gchar const * descr =
1650         "<ui>"
1651         "  <toolbar name='ToolToolbar'>"
1652         "    <toolitem action='ToolSelector' />"
1653         "    <toolitem action='ToolNode' />"
1654         "    <toolitem action='ToolTweak' />"
1655         "    <toolitem action='ToolSpray' />"
1656         "    <toolitem action='ToolZoom' />"
1657         "    <toolitem action='ToolRect' />"
1658         "    <toolitem action='Tool3DBox' />"
1659         "    <toolitem action='ToolArc' />"
1660         "    <toolitem action='ToolStar' />"
1661         "    <toolitem action='ToolSpiral' />"
1662         "    <toolitem action='ToolPencil' />"
1663         "    <toolitem action='ToolPen' />"
1664         "    <toolitem action='ToolCalligraphic' />"
1665         "    <toolitem action='ToolEraser' />"
1666 //        "    <toolitem action='ToolLPETool' />"
1667         "    <toolitem action='ToolPaintBucket' />"
1668         "    <toolitem action='ToolText' />"
1669         "    <toolitem action='ToolConnector' />"
1670         "    <toolitem action='ToolGradient' />"
1671         "    <toolitem action='ToolDropper' />"
1672         "  </toolbar>"
1673         "</ui>";
1674     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1675     GtkUIManager* mgr = gtk_ui_manager_new();
1676     GError* errVal = 0;
1677     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1679     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1680     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1682     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1683     if ( prefs->getBool("/toolbox/icononly", true) ) {
1684         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1685     }
1686     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1687     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1689     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1690     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1692     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1694     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1695     if ( child ) {
1696         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1697     }
1699     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1700 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1704 static void
1705 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1707     gchar const *const tname = ( eventcontext
1708                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1709                                  : NULL );
1710     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1712     for (int i = 0 ; tools[i].type_name ; i++ ) {
1713         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1714         if ( act ) {
1715             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1716             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1717             if ( verbAct ) {
1718                 verbAct->set_active(setActive);
1719             }
1720         }
1721     }
1724 static void
1725 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1727     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1728     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1729     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1730     GtkUIManager* mgr = gtk_ui_manager_new();
1731     GError* errVal = 0;
1732     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1733     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1735     std::map<std::string, GtkWidget*> dataHolders;
1737     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1738         if ( aux_toolboxes[i].prep_func ) {
1739             // converted to GtkActions and UIManager
1741             GtkWidget* kludge = gtk_toolbar_new();
1742             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1743             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1744             dataHolders[aux_toolboxes[i].type_name] = kludge;
1745             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1746         } else {
1748             GtkWidget *sub_toolbox = 0;
1749             if (aux_toolboxes[i].create_func == NULL)
1750                 sub_toolbox = sp_empty_toolbox_new(desktop);
1751             else {
1752                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1753             }
1755             gtk_size_group_add_widget( grouper, sub_toolbox );
1757             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1758             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1760         }
1761     }
1763     // Second pass to create toolbars *after* all GtkActions are created
1764     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1765         if ( aux_toolboxes[i].prep_func ) {
1766             // converted to GtkActions and UIManager
1768             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1770             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1771             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1773             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1774             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1775             g_free( tmp );
1776             tmp = 0;
1778             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1779                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1780             }
1782             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1783             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1785             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1787             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1788                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1789                 swatch->setDesktop( desktop );
1790                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1791                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1792                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1793                 gtk_table_attach( GTK_TABLE(holder), swatch_, 1, 2, 0, 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), AUX_BETWEEN_BUTTON_GROUPS, 0 );
1794             }
1796             gtk_widget_show_all( holder );
1797             sp_set_font_size_smaller( holder );
1799             gtk_size_group_add_widget( grouper, holder );
1801             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1802             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1803         }
1804     }
1806     g_object_unref( G_OBJECT(grouper) );
1809 static void
1810 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1812     gchar const *tname = ( eventcontext
1813                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1814                            : NULL );
1815     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1816         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1817         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1818             gtk_widget_show_all(sub_toolbox);
1819             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1820         } else {
1821             gtk_widget_hide(sub_toolbox);
1822         }
1823     }
1826 static void
1827 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1829     gchar const * descr =
1830         "<ui>"
1831         "  <toolbar name='CommandsToolbar'>"
1832         "    <toolitem action='FileNew' />"
1833         "    <toolitem action='FileOpen' />"
1834         "    <toolitem action='FileSave' />"
1835         "    <toolitem action='FilePrint' />"
1836         "    <separator />"
1837         "    <toolitem action='FileImport' />"
1838         "    <toolitem action='FileExport' />"
1839         "    <separator />"
1840         "    <toolitem action='EditUndo' />"
1841         "    <toolitem action='EditRedo' />"
1842         "    <separator />"
1843         "    <toolitem action='EditCopy' />"
1844         "    <toolitem action='EditCut' />"
1845         "    <toolitem action='EditPaste' />"
1846         "    <separator />"
1847         "    <toolitem action='ZoomSelection' />"
1848         "    <toolitem action='ZoomDrawing' />"
1849         "    <toolitem action='ZoomPage' />"
1850         "    <separator />"
1851         "    <toolitem action='EditDuplicate' />"
1852         "    <toolitem action='EditClone' />"
1853         "    <toolitem action='EditUnlinkClone' />"
1854         "    <separator />"
1855         "    <toolitem action='SelectionGroup' />"
1856         "    <toolitem action='SelectionUnGroup' />"
1857         "    <separator />"
1858         "    <toolitem action='DialogFillStroke' />"
1859         "    <toolitem action='DialogText' />"
1860         "    <toolitem action='DialogLayers' />"
1861         "    <toolitem action='DialogXMLEditor' />"
1862         "    <toolitem action='DialogAlignDistribute' />"
1863         "    <separator />"
1864         "    <toolitem action='DialogPreferences' />"
1865         "    <toolitem action='DialogDocumentProperties' />"
1866         "  </toolbar>"
1867         "</ui>";
1868     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1869     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1871     GtkUIManager* mgr = gtk_ui_manager_new();
1872     GError* errVal = 0;
1874     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1875     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1877     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1878     if ( prefs->getBool("/toolbox/icononly", true) ) {
1879         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1880     }
1882     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1883     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1885     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1886     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1889     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1891     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1892     if ( child ) {
1893         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1894     }
1896     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1899 static void
1900 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1904 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1906     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1907         return;
1908     }
1910     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1911     g_assert(ptr != NULL);
1913     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1914     SPNamedView *nv = sp_desktop_namedview(dt);
1915     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1917     if (dt == NULL || nv == NULL) {
1918         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1919         return;
1920     }
1922     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1924     if (repr == NULL) {
1925         g_warning("This namedview doesn't have a xml representation attached!");
1926         return;
1927     }
1929     bool saved = sp_document_get_undo_sensitive(doc);
1930     sp_document_set_undo_sensitive(doc, false);
1932     bool v = false;
1933     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1935     switch (attr) {
1936         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1937             dt->toggleSnapGlobal();
1938             break;
1939         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1940             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1941             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1942             break;
1943         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1944             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1945             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1946             break;
1947         case SP_ATTR_INKSCAPE_BBOX_NODES:
1948             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1949             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1950             break;
1951         case SP_ATTR_INKSCAPE_SNAP_NODES:
1952             v = nv->snap_manager.snapprefs.getSnapModeNode();
1953             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1954             break;
1955         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1956             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1957             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1958             break;
1959         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1960             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1961             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1962             break;
1963         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1964             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1965             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1966             break;
1967         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1968             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1969             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1970             break;
1971         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1972             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1973             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1974             break;
1975         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1976             v = nv->snap_manager.snapprefs.getSnapToGrids();
1977             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1978             break;
1979         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1980             v = nv->snap_manager.snapprefs.getSnapToGuides();
1981             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1982             break;
1983         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1984             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1985             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1986             break;
1987             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1988               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1989               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1990               break;*/
1991         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1992             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1993             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1994             break;
1995         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1996             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1997             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1998             break;
1999         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2000             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2001             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2002             break;
2003         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2004             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2005             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2006             break;
2007         default:
2008             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2009             break;
2010     }
2012     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2013     doc->setModifiedSinceSave();
2015     sp_document_set_undo_sensitive(doc, saved);
2018 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2020     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2021     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2023     gchar const * descr =
2024         "<ui>"
2025         "  <toolbar name='SnapToolbar'>"
2026         "    <toolitem action='ToggleSnapGlobal' />"
2027         "    <separator />"
2028         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2029         "    <toolitem action='ToggleSnapToBBoxPath' />"
2030         "    <toolitem action='ToggleSnapToBBoxNode' />"
2031         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2032         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2033         "    <separator />"
2034         "    <toolitem action='ToggleSnapFromNode' />"
2035         "    <toolitem action='ToggleSnapToItemPath' />"
2036         "    <toolitem action='ToggleSnapToPathIntersections' />"
2037         "    <toolitem action='ToggleSnapToItemNode' />"
2038         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2039         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2040         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2041         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2042         "    <separator />"
2043         "    <toolitem action='ToggleSnapToPageBorder' />"
2044         "    <toolitem action='ToggleSnapToGrids' />"
2045         "    <toolitem action='ToggleSnapToGuides' />"
2046         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2047         "  </toolbar>"
2048         "</ui>";
2050     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2052     {
2053         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2054                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2055                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2057         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2058         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2059     }
2061     {
2062         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2063                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2064                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2066         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2067         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2068     }
2070     {
2071         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2072                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2073                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2075         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2076         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2077     }
2079     {
2080         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2081                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2082                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2084         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2085         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2086     }
2088     {
2089         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2090                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2091                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2092                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2094         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2095         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2096     }
2098     {
2099         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2100                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2101                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2103         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2104         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2105     }
2107     {
2108         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2109                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2111         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2112         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2113     }
2115     {
2116         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2117                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2118                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2120         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2121         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2122     }
2124     {
2125         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2126                                                      _("Path intersections"), _("Snap to path intersections"),
2127                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2129         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2130         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2131     }
2133     {
2134         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2135                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2136                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2138         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2139         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2140     }
2142     {
2143         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2144                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2145                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2147         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2148         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2149     }
2151     {
2152         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2153                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2154                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2156         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2157         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2158     }
2160     {
2161         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2162                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2163                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2165         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2166         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2167     }
2169     {
2170         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2171                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2172                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2174         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2175         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2176     }
2178     {
2179         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2180                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2181                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2183         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2184         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2185     }
2187     {
2188         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2189                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2190                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2192         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2193         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2194     }
2196     {
2197         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2198                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2199                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2201         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2202         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2203     }
2205     /*{
2206       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2207       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2208       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2209       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2211       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2212       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2213       }*/
2215     GtkUIManager* mgr = gtk_ui_manager_new();
2216     GError* errVal = 0;
2218     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2219     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2221     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2222     if ( prefs->getBool("/toolbox/icononly", true) ) {
2223         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2224     }
2226     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2227     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2229     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2230     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2232     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2234     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2235     if ( child ) {
2236         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2237     }
2239     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2243 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2245     g_assert(desktop != NULL);
2246     g_assert(toolbox != NULL);
2248     SPNamedView *nv = sp_desktop_namedview(desktop);
2249     if (nv == NULL) {
2250         g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2251         return;
2252     }
2254     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2256     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2257     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2258     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2259     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2260     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2261     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2262     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2263     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2264     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2265     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2266     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2267     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2268     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2269     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2270     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2271     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2272     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2273     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2276     if (!act1) {
2277         return; // The snap actions haven't been defined yet (might be the case during startup)
2278     }
2280     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2281     // changes in our document because we're only updating the UI;
2282     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2283     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2285     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2286     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2288     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2289     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2290     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2292     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2293     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2294     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2295     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2296     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2297     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2298     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2299     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2301     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2302     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2303     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2305     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2306     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2307     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2308     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2309     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2310     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2311     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2312     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2313     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2314     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2315     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2316     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2317     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2318     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2319     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2321     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2322     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2323     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2324     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2326     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2327     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2328     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2329     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2332     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2335 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2337     gtk_widget_show(toolbox_toplevel);
2338     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2340     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2341     if (!shown_toolbox) {
2342         return;
2343     }
2344     gtk_widget_show(toolbox);
2346     gtk_widget_show_all(shown_toolbox);
2349 static GtkWidget *
2350 sp_empty_toolbox_new(SPDesktop *desktop)
2352     GtkWidget *tbl = gtk_toolbar_new();
2353     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2354     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2356     gtk_widget_show_all(tbl);
2357     sp_set_font_size_smaller (tbl);
2359     return tbl;
2362 #define MODE_LABEL_WIDTH 70
2364 //########################
2365 //##       Star         ##
2366 //########################
2368 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2370     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2372     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2373         // do not remember prefs if this call is initiated by an undo change, because undoing object
2374         // creation sets bogus values to its attributes before it is deleted
2375         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2376         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2377     }
2379     // quit if run by the attr_changed listener
2380     if (g_object_get_data( dataKludge, "freeze" )) {
2381         return;
2382     }
2384     // in turn, prevent listener from responding
2385     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2387     bool modmade = false;
2389     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2390     GSList const *items = selection->itemList();
2391     for (; items != NULL; items = items->next) {
2392         if (SP_IS_STAR((SPItem *) items->data)) {
2393             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2394             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2395             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2396                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2397                                     + M_PI / (gint)adj->value));
2398             SP_OBJECT((SPItem *) items->data)->updateRepr();
2399             modmade = true;
2400         }
2401     }
2402     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2403                                    _("Star: Change number of corners"));
2405     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2408 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2410     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2412     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2413         if (!IS_NAN(adj->value)) {
2414             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2415             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2416         }
2417     }
2419     // quit if run by the attr_changed listener
2420     if (g_object_get_data( dataKludge, "freeze" )) {
2421         return;
2422     }
2424     // in turn, prevent listener from responding
2425     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2427     bool modmade = false;
2428     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2429     GSList const *items = selection->itemList();
2430     for (; items != NULL; items = items->next) {
2431         if (SP_IS_STAR((SPItem *) items->data)) {
2432             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2434             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2435             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2436             if (r2 < r1) {
2437                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2438             } else {
2439                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2440             }
2442             SP_OBJECT((SPItem *) items->data)->updateRepr();
2443             modmade = true;
2444         }
2445     }
2447     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2448                                    _("Star: Change spoke ratio"));
2450     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2453 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2455     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2456     bool flat = ege_select_one_action_get_active( act ) == 0;
2458     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2459         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2460         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2461     }
2463     // quit if run by the attr_changed listener
2464     if (g_object_get_data( dataKludge, "freeze" )) {
2465         return;
2466     }
2468     // in turn, prevent listener from responding
2469     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2471     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2472     GSList const *items = selection->itemList();
2473     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2474     bool modmade = false;
2476     if ( prop_action ) {
2477         gtk_action_set_sensitive( prop_action, !flat );
2478     }
2480     for (; items != NULL; items = items->next) {
2481         if (SP_IS_STAR((SPItem *) items->data)) {
2482             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2483             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2484             SP_OBJECT((SPItem *) items->data)->updateRepr();
2485             modmade = true;
2486         }
2487     }
2489     if (modmade) {
2490         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2491                          flat ? _("Make polygon") : _("Make star"));
2492     }
2494     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2497 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2499     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2501     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2502         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2503         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2504     }
2506     // quit if run by the attr_changed listener
2507     if (g_object_get_data( dataKludge, "freeze" )) {
2508         return;
2509     }
2511     // in turn, prevent listener from responding
2512     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2514     bool modmade = false;
2516     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2517     GSList const *items = selection->itemList();
2518     for (; items != NULL; items = items->next) {
2519         if (SP_IS_STAR((SPItem *) items->data)) {
2520             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2521             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2522             SP_OBJECT(items->data)->updateRepr();
2523             modmade = true;
2524         }
2525     }
2526     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2527                                    _("Star: Change rounding"));
2529     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2532 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2534     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2536     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2537         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2538         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2539     }
2541     // quit if run by the attr_changed listener
2542     if (g_object_get_data( dataKludge, "freeze" )) {
2543         return;
2544     }
2546     // in turn, prevent listener from responding
2547     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2549     bool modmade = false;
2551     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2552     GSList const *items = selection->itemList();
2553     for (; items != NULL; items = items->next) {
2554         if (SP_IS_STAR((SPItem *) items->data)) {
2555             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2556             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2557             SP_OBJECT(items->data)->updateRepr();
2558             modmade = true;
2559         }
2560     }
2561     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2562                                    _("Star: Change randomization"));
2564     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2568 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2569                                        gchar const */*old_value*/, gchar const */*new_value*/,
2570                                        bool /*is_interactive*/, gpointer data)
2572     GtkWidget *tbl = GTK_WIDGET(data);
2574     // quit if run by the _changed callbacks
2575     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2576         return;
2577     }
2579     // in turn, prevent callbacks from responding
2580     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2582     GtkAdjustment *adj = 0;
2584     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2585     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2587     if (!strcmp(name, "inkscape:randomized")) {
2588         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2589         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2590     } else if (!strcmp(name, "inkscape:rounded")) {
2591         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2592         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2593     } else if (!strcmp(name, "inkscape:flatsided")) {
2594         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2595         char const *flatsides = repr->attribute("inkscape:flatsided");
2596         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2597         if ( flatsides && !strcmp(flatsides,"false") ) {
2598             ege_select_one_action_set_active( flat_action, 1 );
2599             gtk_action_set_sensitive( prop_action, TRUE );
2600         } else {
2601             ege_select_one_action_set_active( flat_action, 0 );
2602             gtk_action_set_sensitive( prop_action, FALSE );
2603         }
2604     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2605         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2606         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2607         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2608         if (r2 < r1) {
2609             gtk_adjustment_set_value(adj, r2/r1);
2610         } else {
2611             gtk_adjustment_set_value(adj, r1/r2);
2612         }
2613     } else if (!strcmp(name, "sodipodi:sides")) {
2614         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2615         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2616     }
2618     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2622 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2624     NULL, /* child_added */
2625     NULL, /* child_removed */
2626     star_tb_event_attr_changed,
2627     NULL, /* content_changed */
2628     NULL  /* order_changed */
2629 };
2632 /**
2633  *  \param selection Should not be NULL.
2634  */
2635 static void
2636 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2638     int n_selected = 0;
2639     Inkscape::XML::Node *repr = NULL;
2641     purge_repr_listener( tbl, tbl );
2643     for (GSList const *items = selection->itemList();
2644          items != NULL;
2645          items = items->next)
2646     {
2647         if (SP_IS_STAR((SPItem *) items->data)) {
2648             n_selected++;
2649             repr = SP_OBJECT_REPR((SPItem *) items->data);
2650         }
2651     }
2653     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2655     if (n_selected == 0) {
2656         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2657     } else if (n_selected == 1) {
2658         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2660         if (repr) {
2661             g_object_set_data( tbl, "repr", repr );
2662             Inkscape::GC::anchor(repr);
2663             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2664             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2665         }
2666     } else {
2667         // FIXME: implement averaging of all parameters for multiple selected stars
2668         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2669         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2670     }
2674 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2676     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2677     // callbacks to lump all the changes for all selected objects in one undo step
2679     GtkAdjustment *adj = 0;
2681     // fixme: make settable in prefs!
2682     gint mag = 5;
2683     gdouble prop = 0.5;
2684     gboolean flat = FALSE;
2685     gdouble randomized = 0;
2686     gdouble rounded = 0;
2688     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2689     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2691     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2692     gtk_action_set_sensitive( sb2, !flat );
2694     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2695     gtk_adjustment_set_value(adj, mag);
2696     gtk_adjustment_value_changed(adj);
2698     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2699     gtk_adjustment_set_value(adj, prop);
2700     gtk_adjustment_value_changed(adj);
2702     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2703     gtk_adjustment_set_value(adj, rounded);
2704     gtk_adjustment_value_changed(adj);
2706     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2707     gtk_adjustment_set_value(adj, randomized);
2708     gtk_adjustment_value_changed(adj);
2712 void
2713 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2715     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2716     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2717     GtkWidget *l = gtk_label_new(NULL);
2718     gtk_label_set_markup(GTK_LABEL(l), title);
2719     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2720     if ( GTK_IS_TOOLBAR(tbl) ) {
2721         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2722     } else {
2723         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2724     }
2725     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2729 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2731     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2733     {
2734         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2735         ege_output_action_set_use_markup( act, TRUE );
2736         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2737         g_object_set_data( holder, "mode_action", act );
2738     }
2740     {
2741         EgeAdjustmentAction* eact = 0;
2742         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2743         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2745         /* Flatsided checkbox */
2746         {
2747             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2749             GtkTreeIter iter;
2750             gtk_list_store_append( model, &iter );
2751             gtk_list_store_set( model, &iter,
2752                                 0, _("Polygon"),
2753                                 1, _("Regular polygon (with one handle) instead of a star"),
2754                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2755                                 -1 );
2757             gtk_list_store_append( model, &iter );
2758             gtk_list_store_set( model, &iter,
2759                                 0, _("Star"),
2760                                 1, _("Star instead of a regular polygon (with one handle)"),
2761                                 2, INKSCAPE_ICON_DRAW_STAR,
2762                                 -1 );
2764             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2765             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2766             g_object_set_data( holder, "flat_action", act );
2768             ege_select_one_action_set_appearance( act, "full" );
2769             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2770             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2771             ege_select_one_action_set_icon_column( act, 2 );
2772             ege_select_one_action_set_icon_size( act, secondarySize );
2773             ege_select_one_action_set_tooltip_column( act, 1  );
2775             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2776             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2777         }
2779         /* Magnitude */
2780         {
2781         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2782         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2783         eact = create_adjustment_action( "MagnitudeAction",
2784                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2785                                          "/tools/shapes/star/magnitude", 3,
2786                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2787                                          3, 1024, 1, 5,
2788                                          labels, values, G_N_ELEMENTS(labels),
2789                                          sp_stb_magnitude_value_changed,
2790                                          1.0, 0 );
2791         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2792         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2793         }
2795         /* Spoke ratio */
2796         {
2797         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2798         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2799         eact = create_adjustment_action( "SpokeAction",
2800                                          _("Spoke ratio"), _("Spoke ratio:"),
2801                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2802                                          // Base radius is the same for the closest handle.
2803                                          _("Base radius to tip radius ratio"),
2804                                          "/tools/shapes/star/proportion", 0.5,
2805                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2806                                          0.01, 1.0, 0.01, 0.1,
2807                                          labels, values, G_N_ELEMENTS(labels),
2808                                          sp_stb_proportion_value_changed );
2809         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2810         g_object_set_data( holder, "prop_action", eact );
2811         }
2813         if ( !isFlatSided ) {
2814             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2815         } else {
2816             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2817         }
2819         /* Roundedness */
2820         {
2821         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2822         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2823         eact = create_adjustment_action( "RoundednessAction",
2824                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2825                                          "/tools/shapes/star/rounded", 0.0,
2826                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2827                                          -10.0, 10.0, 0.01, 0.1,
2828                                          labels, values, G_N_ELEMENTS(labels),
2829                                          sp_stb_rounded_value_changed );
2830         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2831         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2832         }
2834         /* Randomization */
2835         {
2836         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2837         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2838         eact = create_adjustment_action( "RandomizationAction",
2839                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2840                                          "/tools/shapes/star/randomized", 0.0,
2841                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2842                                          -10.0, 10.0, 0.001, 0.01,
2843                                          labels, values, G_N_ELEMENTS(labels),
2844                                          sp_stb_randomized_value_changed, 0.1, 3 );
2845         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2846         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2847         }
2848     }
2850     {
2851         /* Reset */
2852         {
2853             GtkAction* act = gtk_action_new( "StarResetAction",
2854                                              _("Defaults"),
2855                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2856                                              GTK_STOCK_CLEAR );
2857             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2858             gtk_action_group_add_action( mainActions, act );
2859             gtk_action_set_sensitive( act, TRUE );
2860         }
2861     }
2863     sigc::connection *connection = new sigc::connection(
2864         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2865         );
2866     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2867     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2871 //########################
2872 //##       Rect         ##
2873 //########################
2875 static void sp_rtb_sensitivize( GObject *tbl )
2877     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2878     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2879     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2881     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2882         gtk_action_set_sensitive( not_rounded, FALSE );
2883     } else {
2884         gtk_action_set_sensitive( not_rounded, TRUE );
2885     }
2889 static void
2890 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2891                           void (*setter)(SPRect *, gdouble))
2893     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2895     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2896     SPUnit const *unit = tracker->getActiveUnit();
2898     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2899         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2900         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2901     }
2903     // quit if run by the attr_changed listener
2904     if (g_object_get_data( tbl, "freeze" )) {
2905         return;
2906     }
2908     // in turn, prevent listener from responding
2909     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2911     bool modmade = false;
2912     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2913     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2914         if (SP_IS_RECT(items->data)) {
2915             if (adj->value != 0) {
2916                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2917             } else {
2918                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2919             }
2920             modmade = true;
2921         }
2922     }
2924     sp_rtb_sensitivize( tbl );
2926     if (modmade) {
2927         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2928                                    _("Change rectangle"));
2929     }
2931     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2934 static void
2935 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2937     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2940 static void
2941 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2943     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2946 static void
2947 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2949     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2952 static void
2953 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2955     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2960 static void
2961 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2963     GtkAdjustment *adj = 0;
2965     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2966     gtk_adjustment_set_value(adj, 0.0);
2967     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2968     gtk_adjustment_value_changed(adj);
2970     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2971     gtk_adjustment_set_value(adj, 0.0);
2972     gtk_adjustment_value_changed(adj);
2974     sp_rtb_sensitivize( obj );
2977 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2978                                        gchar const */*old_value*/, gchar const */*new_value*/,
2979                                        bool /*is_interactive*/, gpointer data)
2981     GObject *tbl = G_OBJECT(data);
2983     // quit if run by the _changed callbacks
2984     if (g_object_get_data( tbl, "freeze" )) {
2985         return;
2986     }
2988     // in turn, prevent callbacks from responding
2989     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2991     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2992     SPUnit const *unit = tracker->getActiveUnit();
2994     gpointer item = g_object_get_data( tbl, "item" );
2995     if (item && SP_IS_RECT(item)) {
2996         {
2997             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2998             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2999             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3000         }
3002         {
3003             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3004             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3005             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3006         }
3008         {
3009             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3010             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3011             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3012         }
3014         {
3015             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3016             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3017             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3018         }
3019     }
3021     sp_rtb_sensitivize( tbl );
3023     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3027 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3028     NULL, /* child_added */
3029     NULL, /* child_removed */
3030     rect_tb_event_attr_changed,
3031     NULL, /* content_changed */
3032     NULL  /* order_changed */
3033 };
3035 /**
3036  *  \param selection should not be NULL.
3037  */
3038 static void
3039 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3041     int n_selected = 0;
3042     Inkscape::XML::Node *repr = NULL;
3043     SPItem *item = NULL;
3045     if ( g_object_get_data( tbl, "repr" ) ) {
3046         g_object_set_data( tbl, "item", NULL );
3047     }
3048     purge_repr_listener( tbl, tbl );
3050     for (GSList const *items = selection->itemList();
3051          items != NULL;
3052          items = items->next) {
3053         if (SP_IS_RECT((SPItem *) items->data)) {
3054             n_selected++;
3055             item = (SPItem *) items->data;
3056             repr = SP_OBJECT_REPR(item);
3057         }
3058     }
3060     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3062     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3064     if (n_selected == 0) {
3065         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3067         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3068         gtk_action_set_sensitive(w, FALSE);
3069         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3070         gtk_action_set_sensitive(h, FALSE);
3072     } else if (n_selected == 1) {
3073         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3074         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3076         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3077         gtk_action_set_sensitive(w, TRUE);
3078         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3079         gtk_action_set_sensitive(h, TRUE);
3081         if (repr) {
3082             g_object_set_data( tbl, "repr", repr );
3083             g_object_set_data( tbl, "item", item );
3084             Inkscape::GC::anchor(repr);
3085             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3086             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3087         }
3088     } else {
3089         // FIXME: implement averaging of all parameters for multiple selected
3090         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3091         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3092         sp_rtb_sensitivize( tbl );
3093     }
3097 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3099     EgeAdjustmentAction* eact = 0;
3100     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3102     {
3103         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3104         ege_output_action_set_use_markup( act, TRUE );
3105         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3106         g_object_set_data( holder, "mode_action", act );
3107     }
3109     // rx/ry units menu: create
3110     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3111     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3112     // fixme: add % meaning per cent of the width/height
3113     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3114     g_object_set_data( holder, "tracker", tracker );
3116     /* W */
3117     {
3118         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3119         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3120         eact = create_adjustment_action( "RectWidthAction",
3121                                          _("Width"), _("W:"), _("Width of rectangle"),
3122                                          "/tools/shapes/rect/width", 0,
3123                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3124                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3125                                          labels, values, G_N_ELEMENTS(labels),
3126                                          sp_rtb_width_value_changed );
3127         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3128         g_object_set_data( holder, "width_action", eact );
3129         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3130         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3131     }
3133     /* H */
3134     {
3135         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3136         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3137         eact = create_adjustment_action( "RectHeightAction",
3138                                          _("Height"), _("H:"), _("Height of rectangle"),
3139                                          "/tools/shapes/rect/height", 0,
3140                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3141                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3142                                          labels, values, G_N_ELEMENTS(labels),
3143                                          sp_rtb_height_value_changed );
3144         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3145         g_object_set_data( holder, "height_action", eact );
3146         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3147         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3148     }
3150     /* rx */
3151     {
3152         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3153         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3154         eact = create_adjustment_action( "RadiusXAction",
3155                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3156                                          "/tools/shapes/rect/rx", 0,
3157                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3158                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3159                                          labels, values, G_N_ELEMENTS(labels),
3160                                          sp_rtb_rx_value_changed);
3161         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3162         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3163     }
3165     /* ry */
3166     {
3167         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3168         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3169         eact = create_adjustment_action( "RadiusYAction",
3170                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3171                                          "/tools/shapes/rect/ry", 0,
3172                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3173                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3174                                          labels, values, G_N_ELEMENTS(labels),
3175                                          sp_rtb_ry_value_changed);
3176         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3177         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3178     }
3180     // add the units menu
3181     {
3182         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3183         gtk_action_group_add_action( mainActions, act );
3184     }
3186     /* Reset */
3187     {
3188         InkAction* inky = ink_action_new( "RectResetAction",
3189                                           _("Not rounded"),
3190                                           _("Make corners sharp"),
3191                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3192                                           secondarySize );
3193         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3194         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3195         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3196         g_object_set_data( holder, "not_rounded", inky );
3197     }
3199     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3200     sp_rtb_sensitivize( holder );
3202     sigc::connection *connection = new sigc::connection(
3203         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3204         );
3205     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3206     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3209 //########################
3210 //##       3D Box       ##
3211 //########################
3213 // normalize angle so that it lies in the interval [0,360]
3214 static double box3d_normalize_angle (double a) {
3215     double angle = a + ((int) (a/360.0))*360;
3216     if (angle < 0) {
3217         angle += 360.0;
3218     }
3219     return angle;
3222 static void
3223 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3224                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3225     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3226     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3227     //       are reset).
3228     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3230     if (is_infinite) {
3231         gtk_toggle_action_set_active(tact, TRUE);
3232         gtk_action_set_sensitive(act, TRUE);
3234         double angle = persp3d_get_infinite_angle(persp, axis);
3235         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3236             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3237         }
3238     } else {
3239         gtk_toggle_action_set_active(tact, FALSE);
3240         gtk_action_set_sensitive(act, FALSE);
3241     }
3244 static void
3245 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3246     if (!persp_repr) {
3247         g_print ("No perspective given to box3d_resync_toolbar().\n");
3248         return;
3249     }
3251     GtkWidget *tbl = GTK_WIDGET(data);
3252     GtkAdjustment *adj = 0;
3253     GtkAction *act = 0;
3254     GtkToggleAction *tact = 0;
3255     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3256     if (!persp) {
3257         // Hmm, is it an error if this happens?
3258         return;
3259     }
3260     {
3261         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3262         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3263         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3265         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3266     }
3267     {
3268         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3269         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3270         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3272         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3273     }
3274     {
3275         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3276         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3277         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3279         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3280     }
3283 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3284                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3285                                                   bool /*is_interactive*/, gpointer data)
3287     GtkWidget *tbl = GTK_WIDGET(data);
3289     // quit if run by the attr_changed or selection changed listener
3290     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3291         return;
3292     }
3294     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3295     // sp_document_maybe_done() when the document is undo insensitive)
3296     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3298     // TODO: Only update the appropriate part of the toolbar
3299 //    if (!strcmp(name, "inkscape:vp_z")) {
3300         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3301 //    }
3303     Persp3D *persp = persp3d_get_from_repr(repr);
3304     persp3d_update_box_reprs(persp);
3306     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3309 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3311     NULL, /* child_added */
3312     NULL, /* child_removed */
3313     box3d_persp_tb_event_attr_changed,
3314     NULL, /* content_changed */
3315     NULL  /* order_changed */
3316 };
3318 /**
3319  *  \param selection Should not be NULL.
3320  */
3321 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3322 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3323 static void
3324 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3326     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3327     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3328     // update the perspectives with infinite VPs and leave the other ones untouched).
3330     Inkscape::XML::Node *persp_repr = NULL;
3331     purge_repr_listener(tbl, tbl);
3333     SPItem *item = selection->singleItem();
3334     if (item && SP_IS_BOX3D(item)) {
3335         // FIXME: Also deal with multiple selected boxes
3336         SPBox3D *box = SP_BOX3D(item);
3337         Persp3D *persp = box3d_get_perspective(box);
3338         persp_repr = SP_OBJECT_REPR(persp);
3339         if (persp_repr) {
3340             g_object_set_data(tbl, "repr", persp_repr);
3341             Inkscape::GC::anchor(persp_repr);
3342             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3343             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3344         }
3346         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3347         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3348         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3350         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3351         box3d_resync_toolbar(persp_repr, tbl);
3352         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3353     }
3356 static void
3357 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3359     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3360     SPDocument *document = sp_desktop_document(desktop);
3362     // quit if run by the attr_changed or selection changed listener
3363     if (g_object_get_data( dataKludge, "freeze" )) {
3364         return;
3365     }
3367     // in turn, prevent listener from responding
3368     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3370     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3371     if (sel_persps.empty()) {
3372         // this can happen when the document is created; we silently ignore it
3373         return;
3374     }
3375     Persp3D *persp = sel_persps.front();
3377     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3378     SP_OBJECT(persp)->updateRepr();
3380     // TODO: use the correct axis here, too
3381     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3383     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3387 static void
3388 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3390     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3393 static void
3394 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3396     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3399 static void
3400 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3402     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3406 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3408     // TODO: Take all selected perspectives into account
3409     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3410     if (sel_persps.empty()) {
3411         // this can happen when the document is created; we silently ignore it
3412         return;
3413     }
3414     Persp3D *persp = sel_persps.front();
3416     bool set_infinite = gtk_toggle_action_get_active(act);
3417     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3420 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3422     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3425 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3427     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3430 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3432     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3435 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3437     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3438     EgeAdjustmentAction* eact = 0;
3439     SPDocument *document = sp_desktop_document (desktop);
3440     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3442     EgeAdjustmentAction* box3d_angle_x = 0;
3443     EgeAdjustmentAction* box3d_angle_y = 0;
3444     EgeAdjustmentAction* box3d_angle_z = 0;
3446     /* Angle X */
3447     {
3448         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3449         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3450         eact = create_adjustment_action( "3DBoxAngleXAction",
3451                                          _("Angle in X direction"), _("Angle X:"),
3452                                          // Translators: PL is short for 'perspective line'
3453                                          _("Angle of PLs in X direction"),
3454                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3455                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3456                                          -360.0, 360.0, 1.0, 10.0,
3457                                          labels, values, G_N_ELEMENTS(labels),
3458                                          box3d_angle_x_value_changed );
3459         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3460         g_object_set_data( holder, "box3d_angle_x_action", eact );
3461         box3d_angle_x = eact;
3462     }
3464     if (!persp3d_VP_is_finite(persp_impl, Proj::X)) {
3465         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3466     } else {
3467         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3468     }
3471     /* VP X state */
3472     {
3473         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3474                                                       // Translators: VP is short for 'vanishing point'
3475                                                       _("State of VP in X direction"),
3476                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3477                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3478                                                       Inkscape::ICON_SIZE_DECORATION );
3479         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3480         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3481         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3482         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3483         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3484     }
3486     /* Angle Y */
3487     {
3488         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3489         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3490         eact = create_adjustment_action( "3DBoxAngleYAction",
3491                                          _("Angle in Y direction"), _("Angle Y:"),
3492                                          // Translators: PL is short for 'perspective line'
3493                                          _("Angle of PLs in Y direction"),
3494                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3495                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3496                                          -360.0, 360.0, 1.0, 10.0,
3497                                          labels, values, G_N_ELEMENTS(labels),
3498                                          box3d_angle_y_value_changed );
3499         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3500         g_object_set_data( holder, "box3d_angle_y_action", eact );
3501         box3d_angle_y = eact;
3502     }
3504     if (!persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3505         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3506     } else {
3507         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3508     }
3510     /* VP Y state */
3511     {
3512         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3513                                                       // Translators: VP is short for 'vanishing point'
3514                                                       _("State of VP in Y direction"),
3515                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3516                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3517                                                       Inkscape::ICON_SIZE_DECORATION );
3518         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3519         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3520         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3521         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3522         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3523     }
3525     /* Angle Z */
3526     {
3527         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3528         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3529         eact = create_adjustment_action( "3DBoxAngleZAction",
3530                                          _("Angle in Z direction"), _("Angle Z:"),
3531                                          // Translators: PL is short for 'perspective line'
3532                                          _("Angle of PLs in Z direction"),
3533                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3534                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3535                                          -360.0, 360.0, 1.0, 10.0,
3536                                          labels, values, G_N_ELEMENTS(labels),
3537                                          box3d_angle_z_value_changed );
3538         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3539         g_object_set_data( holder, "box3d_angle_z_action", eact );
3540         box3d_angle_z = eact;
3541     }
3543     if (!persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3544         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3545     } else {
3546         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3547     }
3549     /* VP Z state */
3550     {
3551         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3552                                                       // Translators: VP is short for 'vanishing point'
3553                                                       _("State of VP in Z direction"),
3554                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3555                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3556                                                       Inkscape::ICON_SIZE_DECORATION );
3557         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3558         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3559         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3560         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3561         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3562     }
3564     sigc::connection *connection = new sigc::connection(
3565         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3566        );
3567     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3568     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3571 //########################
3572 //##       Spiral       ##
3573 //########################
3575 static void
3576 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3578     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3580     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3581         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3582         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3583     }
3585     // quit if run by the attr_changed listener
3586     if (g_object_get_data( tbl, "freeze" )) {
3587         return;
3588     }
3590     // in turn, prevent listener from responding
3591     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3593     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3595     bool modmade = false;
3596     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3597          items != NULL;
3598          items = items->next)
3599     {
3600         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3601             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3602             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3603             SP_OBJECT((SPItem *) items->data)->updateRepr();
3604             modmade = true;
3605         }
3606     }
3608     g_free(namespaced_name);
3610     if (modmade) {
3611         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3612                                    _("Change spiral"));
3613     }
3615     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3618 static void
3619 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3621     sp_spl_tb_value_changed(adj, tbl, "revolution");
3624 static void
3625 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3627     sp_spl_tb_value_changed(adj, tbl, "expansion");
3630 static void
3631 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3633     sp_spl_tb_value_changed(adj, tbl, "t0");
3636 static void
3637 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3639     GtkWidget *tbl = GTK_WIDGET(obj);
3641     GtkAdjustment *adj;
3643     // fixme: make settable
3644     gdouble rev = 5;
3645     gdouble exp = 1.0;
3646     gdouble t0 = 0.0;
3648     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3649     gtk_adjustment_set_value(adj, rev);
3650     gtk_adjustment_value_changed(adj);
3652     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3653     gtk_adjustment_set_value(adj, exp);
3654     gtk_adjustment_value_changed(adj);
3656     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3657     gtk_adjustment_set_value(adj, t0);
3658     gtk_adjustment_value_changed(adj);
3660     spinbutton_defocus(GTK_OBJECT(tbl));
3664 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3665                                          gchar const */*old_value*/, gchar const */*new_value*/,
3666                                          bool /*is_interactive*/, gpointer data)
3668     GtkWidget *tbl = GTK_WIDGET(data);
3670     // quit if run by the _changed callbacks
3671     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3672         return;
3673     }
3675     // in turn, prevent callbacks from responding
3676     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3678     GtkAdjustment *adj;
3679     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3680     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3682     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3683     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3685     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3686     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3688     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3692 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3693     NULL, /* child_added */
3694     NULL, /* child_removed */
3695     spiral_tb_event_attr_changed,
3696     NULL, /* content_changed */
3697     NULL  /* order_changed */
3698 };
3700 static void
3701 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3703     int n_selected = 0;
3704     Inkscape::XML::Node *repr = NULL;
3706     purge_repr_listener( tbl, tbl );
3708     for (GSList const *items = selection->itemList();
3709          items != NULL;
3710          items = items->next)
3711     {
3712         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3713             n_selected++;
3714             repr = SP_OBJECT_REPR((SPItem *) items->data);
3715         }
3716     }
3718     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3720     if (n_selected == 0) {
3721         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3722     } else if (n_selected == 1) {
3723         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3725         if (repr) {
3726             g_object_set_data( tbl, "repr", repr );
3727             Inkscape::GC::anchor(repr);
3728             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3729             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3730         }
3731     } else {
3732         // FIXME: implement averaging of all parameters for multiple selected
3733         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3734         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3735     }
3739 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3741     EgeAdjustmentAction* eact = 0;
3742     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3744     {
3745         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3746         ege_output_action_set_use_markup( act, TRUE );
3747         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748         g_object_set_data( holder, "mode_action", act );
3749     }
3751     /* Revolution */
3752     {
3753         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3754         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3755         eact = create_adjustment_action( "SpiralRevolutionAction",
3756                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3757                                          "/tools/shapes/spiral/revolution", 3.0,
3758                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3759                                          0.01, 1024.0, 0.1, 1.0,
3760                                          labels, values, G_N_ELEMENTS(labels),
3761                                          sp_spl_tb_revolution_value_changed, 1, 2);
3762         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3763     }
3765     /* Expansion */
3766     {
3767         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3768         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3769         eact = create_adjustment_action( "SpiralExpansionAction",
3770                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3771                                          "/tools/shapes/spiral/expansion", 1.0,
3772                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3773                                          0.0, 1000.0, 0.01, 1.0,
3774                                          labels, values, G_N_ELEMENTS(labels),
3775                                          sp_spl_tb_expansion_value_changed);
3776         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3777     }
3779     /* T0 */
3780     {
3781         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3782         gdouble values[] = {0, 0.5, 0.9};
3783         eact = create_adjustment_action( "SpiralT0Action",
3784                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3785                                          "/tools/shapes/spiral/t0", 0.0,
3786                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3787                                          0.0, 0.999, 0.01, 1.0,
3788                                          labels, values, G_N_ELEMENTS(labels),
3789                                          sp_spl_tb_t0_value_changed);
3790         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3791     }
3793     /* Reset */
3794     {
3795         InkAction* inky = ink_action_new( "SpiralResetAction",
3796                                           _("Defaults"),
3797                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3798                                           GTK_STOCK_CLEAR,
3799                                           secondarySize );
3800         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3801         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3802     }
3805     sigc::connection *connection = new sigc::connection(
3806         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3807         );
3808     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3809     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3812 //########################
3813 //##     Pen/Pencil     ##
3814 //########################
3816 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3817 static Glib::ustring const
3818 freehand_tool_name(GObject *dataKludge)
3820     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3821     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3822              ? "/tools/freehand/pen"
3823              : "/tools/freehand/pencil" );
3826 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3828     gint mode = ege_select_one_action_get_active(act);
3830     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3831     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3833     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3835     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3836     // preparatory work here
3837     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3838         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3839         sp_pen_context_set_polyline_mode(pc);
3840     }
3843 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3845     /* Freehand mode toggle buttons */
3846     {
3847         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3848         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3849         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3851         {
3852             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3854             GtkTreeIter iter;
3855             gtk_list_store_append( model, &iter );
3856             gtk_list_store_set( model, &iter,
3857                                 0, _("Bezier"),
3858                                 1, _("Create regular Bezier path"),
3859                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3860                                 -1 );
3862             gtk_list_store_append( model, &iter );
3863             gtk_list_store_set( model, &iter,
3864                                 0, _("Spiro"),
3865                                 1, _("Create Spiro path"),
3866                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3867                                 -1 );
3869             if (!tool_is_pencil) {
3870                 gtk_list_store_append( model, &iter );
3871                 gtk_list_store_set( model, &iter,
3872                                     0, _("Zigzag"),
3873                                     1, _("Create a sequence of straight line segments"),
3874                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3875                                     -1 );
3877                 gtk_list_store_append( model, &iter );
3878                 gtk_list_store_set( model, &iter,
3879                                     0, _("Paraxial"),
3880                                     1, _("Create a sequence of paraxial line segments"),
3881                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3882                                     -1 );
3883             }
3885             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3886                                                                 "FreehandModeActionPencil" :
3887                                                                 "FreehandModeActionPen",
3888                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3889             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3891             ege_select_one_action_set_appearance( act, "full" );
3892             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3893             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3894             ege_select_one_action_set_icon_column( act, 2 );
3895             ege_select_one_action_set_icon_size( act, secondarySize );
3896             ege_select_one_action_set_tooltip_column( act, 1  );
3898             ege_select_one_action_set_active( act, freehandMode);
3899             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3900         }
3901     }
3904 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3905     gint shape = ege_select_one_action_get_active( act );
3906     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3907     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3910 /**
3911  * \brief Generate the list of freehand advanced shape option entries.
3912  */
3913 GList * freehand_shape_dropdown_items_list() {
3914     GList *glist = NULL;
3916     glist = g_list_append (glist, _("None"));
3917     glist = g_list_append (glist, _("Triangle in"));
3918     glist = g_list_append (glist, _("Triangle out"));
3919     glist = g_list_append (glist, _("Ellipse"));
3920     glist = g_list_append (glist, _("From clipboard"));
3922     return glist;
3925 static void
3926 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3927     /*advanced shape options */
3928     {
3929         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3930         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3932         GList* items = 0;
3933         gint count = 0;
3934         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3935         {
3936             GtkTreeIter iter;
3937             gtk_list_store_append( model, &iter );
3938             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3939             count++;
3940         }
3941         g_list_free( items );
3942         items = 0;
3943         EgeSelectOneAction* act1 = ege_select_one_action_new(
3944             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3945             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3946         g_object_set( act1, "short_label", _("Shape:"), NULL );
3947         ege_select_one_action_set_appearance( act1, "compact" );
3948         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3949         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3950         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3951         g_object_set_data( holder, "shape_action", act1 );
3952     }
3955 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3957     sp_add_freehand_mode_toggle(mainActions, holder, false);
3958     freehand_add_advanced_shape_options(mainActions, holder, false);
3962 static void
3963 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3965     GtkWidget *tbl = GTK_WIDGET(obj);
3967     GtkAdjustment *adj;
3969     // fixme: make settable
3970     gdouble tolerance = 4;
3972     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3973     gtk_adjustment_set_value(adj, tolerance);
3974     gtk_adjustment_value_changed(adj);
3976     spinbutton_defocus(GTK_OBJECT(tbl));
3979 static void
3980 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3982     // quit if run by the attr_changed listener
3983     if (g_object_get_data( tbl, "freeze" )) {
3984         return;
3985     }
3986     // in turn, prevent listener from responding
3987     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3988     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3989     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3990     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3993 /*
3994 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3995 public:
3996     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3997     {
3998         g_object_set_data(_obj, "prefobserver", this);
3999     }
4000     virtual ~PencilToleranceObserver() {
4001         if (g_object_get_data(_obj, "prefobserver") == this) {
4002             g_object_set_data(_obj, "prefobserver", NULL);
4003         }
4004     }
4005     virtual void notify(Inkscape::Preferences::Entry const &val) {
4006         GObject* tbl = _obj;
4007         if (g_object_get_data( tbl, "freeze" )) {
4008             return;
4009         }
4010         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4012         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4014         double v = val.getDouble(adj->value);
4015         gtk_adjustment_set_value(adj, v);
4016         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4017     }
4018 private:
4019     GObject *_obj;
4020 };
4021 */
4023 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4025     sp_add_freehand_mode_toggle(mainActions, holder, true);
4027     EgeAdjustmentAction* eact = 0;
4029     /* Tolerance */
4030     {
4031         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4032         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4033         eact = create_adjustment_action( "PencilToleranceAction",
4034                                          _("Smoothing:"), _("Smoothing: "),
4035                  _("How much smoothing (simplifying) is applied to the line"),
4036                                          "/tools/freehand/pencil/tolerance",
4037                                          3.0,
4038                                          GTK_WIDGET(desktop->canvas), NULL,
4039                                          holder, TRUE, "altx-pencil",
4040                                          1, 100.0, 0.5, 1.0,
4041                                          labels, values, G_N_ELEMENTS(labels),
4042                                          sp_pencil_tb_tolerance_value_changed,
4043                                          1, 2);
4044         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4045         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4046     }
4048     /* advanced shape options */
4049     freehand_add_advanced_shape_options(mainActions, holder, true);
4051     /* Reset */
4052     {
4053         InkAction* inky = ink_action_new( "PencilResetAction",
4054                                           _("Defaults"),
4055                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4056                                           GTK_STOCK_CLEAR,
4057                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4058         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4059         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4060     }
4062     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4067 //########################
4068 //##       Tweak        ##
4069 //########################
4071 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4073     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4077 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4079     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4080     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4083 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4085     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4086     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4089 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4091     int mode = ege_select_one_action_get_active( act );
4092     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4093     prefs->setInt("/tools/tweak/mode", mode);
4095     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4096     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4097     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4098     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4099     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4100     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4101     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4102         if (doh) gtk_action_set_sensitive (doh, TRUE);
4103         if (dos) gtk_action_set_sensitive (dos, TRUE);
4104         if (dol) gtk_action_set_sensitive (dol, TRUE);
4105         if (doo) gtk_action_set_sensitive (doo, TRUE);
4106         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4107         if (fid) gtk_action_set_sensitive (fid, FALSE);
4108     } else {
4109         if (doh) gtk_action_set_sensitive (doh, FALSE);
4110         if (dos) gtk_action_set_sensitive (dos, FALSE);
4111         if (dol) gtk_action_set_sensitive (dol, FALSE);
4112         if (doo) gtk_action_set_sensitive (doo, FALSE);
4113         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4114         if (fid) gtk_action_set_sensitive (fid, TRUE);
4115     }
4118 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4120     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4121     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4124 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4125     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4126     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4128 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4129     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4130     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4132 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4133     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4134     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4136 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4137     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4138     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4141 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4143     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4144     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4146     {
4147         /* Width */
4148         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4149         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4150         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4151                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4152                                                               "/tools/tweak/width", 15,
4153                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4154                                                               1, 100, 1.0, 10.0,
4155                                                               labels, values, G_N_ELEMENTS(labels),
4156                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4157         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4158         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4159         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4160     }
4163     {
4164         /* Force */
4165         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4166         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4167         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4168                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4169                                                               "/tools/tweak/force", 20,
4170                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4171                                                               1, 100, 1.0, 10.0,
4172                                                               labels, values, G_N_ELEMENTS(labels),
4173                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4174         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4175         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4176         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4177     }
4179     /* Mode */
4180     {
4181         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4183         GtkTreeIter iter;
4184         gtk_list_store_append( model, &iter );
4185         gtk_list_store_set( model, &iter,
4186                             0, _("Move mode"),
4187                             1, _("Move objects in any direction"),
4188                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4189                             -1 );
4191         gtk_list_store_append( model, &iter );
4192         gtk_list_store_set( model, &iter,
4193                             0, _("Move in/out mode"),
4194                             1, _("Move objects towards cursor; with Shift from cursor"),
4195                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4196                             -1 );
4198         gtk_list_store_append( model, &iter );
4199         gtk_list_store_set( model, &iter,
4200                             0, _("Move jitter mode"),
4201                             1, _("Move objects in random directions"),
4202                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4203                             -1 );
4205         gtk_list_store_append( model, &iter );
4206         gtk_list_store_set( model, &iter,
4207                             0, _("Scale mode"),
4208                             1, _("Shrink objects, with Shift enlarge"),
4209                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4210                             -1 );
4212         gtk_list_store_append( model, &iter );
4213         gtk_list_store_set( model, &iter,
4214                             0, _("Rotate mode"),
4215                             1, _("Rotate objects, with Shift counterclockwise"),
4216                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4217                             -1 );
4219         gtk_list_store_append( model, &iter );
4220         gtk_list_store_set( model, &iter,
4221                             0, _("Duplicate/delete mode"),
4222                             1, _("Duplicate objects, with Shift delete"),
4223                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4224                             -1 );
4226         gtk_list_store_append( model, &iter );
4227         gtk_list_store_set( model, &iter,
4228                             0, _("Push mode"),
4229                             1, _("Push parts of paths in any direction"),
4230                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4231                             -1 );
4233         gtk_list_store_append( model, &iter );
4234         gtk_list_store_set( model, &iter,
4235                             0, _("Shrink/grow mode"),
4236                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4237                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4238                             -1 );
4240         gtk_list_store_append( model, &iter );
4241         gtk_list_store_set( model, &iter,
4242                             0, _("Attract/repel mode"),
4243                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4244                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4245                             -1 );
4247         gtk_list_store_append( model, &iter );
4248         gtk_list_store_set( model, &iter,
4249                             0, _("Roughen mode"),
4250                             1, _("Roughen parts of paths"),
4251                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4252                             -1 );
4254         gtk_list_store_append( model, &iter );
4255         gtk_list_store_set( model, &iter,
4256                             0, _("Color paint mode"),
4257                             1, _("Paint the tool's color upon selected objects"),
4258                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4259                             -1 );
4261         gtk_list_store_append( model, &iter );
4262         gtk_list_store_set( model, &iter,
4263                             0, _("Color jitter mode"),
4264                             1, _("Jitter the colors of selected objects"),
4265                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4266                             -1 );
4268         gtk_list_store_append( model, &iter );
4269         gtk_list_store_set( model, &iter,
4270                             0, _("Blur mode"),
4271                             1, _("Blur selected objects more; with Shift, blur less"),
4272                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4273                             -1 );
4276         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4277         g_object_set( act, "short_label", _("Mode:"), NULL );
4278         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4279         g_object_set_data( holder, "mode_action", act );
4281         ege_select_one_action_set_appearance( act, "full" );
4282         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4283         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4284         ege_select_one_action_set_icon_column( act, 2 );
4285         ege_select_one_action_set_icon_size( act, secondarySize );
4286         ege_select_one_action_set_tooltip_column( act, 1  );
4288         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4289         ege_select_one_action_set_active( act, mode );
4290         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4292         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4293     }
4295     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4297     {
4298         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4299         ege_output_action_set_use_markup( act, TRUE );
4300         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4301         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4302             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4303         g_object_set_data( holder, "tweak_channels_label", act);
4304     }
4306     {
4307         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4308                                                       _("Hue"),
4309                                                       _("In color mode, act on objects' hue"),
4310                                                       NULL,
4311                                                       Inkscape::ICON_SIZE_DECORATION );
4312         //TRANSLATORS:  "H" here stands for hue
4313         g_object_set( act, "short_label", _("H"), NULL );
4314         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4315         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4316         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4317         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4318             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4319         g_object_set_data( holder, "tweak_doh", act);
4320     }
4321     {
4322         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4323                                                       _("Saturation"),
4324                                                       _("In color mode, act on objects' saturation"),
4325                                                       NULL,
4326                                                       Inkscape::ICON_SIZE_DECORATION );
4327         //TRANSLATORS: "S" here stands for Saturation
4328         g_object_set( act, "short_label", _("S"), NULL );
4329         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4330         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4331         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4332         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4333             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4334         g_object_set_data( holder, "tweak_dos", act );
4335     }
4336     {
4337         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4338                                                       _("Lightness"),
4339                                                       _("In color mode, act on objects' lightness"),
4340                                                       NULL,
4341                                                       Inkscape::ICON_SIZE_DECORATION );
4342         //TRANSLATORS: "L" here stands for Lightness
4343         g_object_set( act, "short_label", _("L"), NULL );
4344         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4345         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4346         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4347         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4348             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4349         g_object_set_data( holder, "tweak_dol", act );
4350     }
4351     {
4352         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4353                                                       _("Opacity"),
4354                                                       _("In color mode, act on objects' opacity"),
4355                                                       NULL,
4356                                                       Inkscape::ICON_SIZE_DECORATION );
4357         //TRANSLATORS: "O" here stands for Opacity
4358         g_object_set( act, "short_label", _("O"), NULL );
4359         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4360         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4361         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4362         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4363             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4364         g_object_set_data( holder, "tweak_doo", act );
4365     }
4367     {   /* Fidelity */
4368         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4369         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4370         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4371                                                               _("Fidelity"), _("Fidelity:"),
4372                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4373                                                               "/tools/tweak/fidelity", 50,
4374                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4375                                                               1, 100, 1.0, 10.0,
4376                                                               labels, values, G_N_ELEMENTS(labels),
4377                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4378         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4379         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4380         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4381             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4382         g_object_set_data( holder, "tweak_fidelity", eact );
4383     }
4386     /* Use Pressure button */
4387     {
4388         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4389                                                       _("Pressure"),
4390                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4391                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4392                                                       Inkscape::ICON_SIZE_DECORATION );
4393         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4394         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4395         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4396     }
4401 //########################
4402 //##       Spray        ##
4403 //########################
4405 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4407     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4408     prefs->setDouble( "/tools/spray/width", adj->value );
4411 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4413     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4414     prefs->setDouble( "/tools/spray/mean", adj->value );
4417 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4419     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4420     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4423 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4425     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4426     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4429 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4431     int mode = ege_select_one_action_get_active( act );
4432     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4433     prefs->setInt("/tools/spray/mode", mode);
4436 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4438     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439     prefs->setDouble( "/tools/spray/population", adj->value );
4442 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4444     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4445     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4448 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4450     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4451     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4455 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4457     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4458     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460     {
4461         /* Width */
4462         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4463         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4464         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4465                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4466                                                               "/tools/spray/width", 15,
4467                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4468                                                               1, 100, 1.0, 10.0,
4469                                                               labels, values, G_N_ELEMENTS(labels),
4470                                                               sp_spray_width_value_changed,  1, 0 );
4471         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4472         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4473         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4474     }
4476     {
4477         /* Mean */
4478         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4479         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4480         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4481                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4482                                                               "/tools/spray/mean", 0,
4483                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4484                                                               0, 100, 1.0, 10.0,
4485                                                               labels, values, G_N_ELEMENTS(labels),
4486                                                               sp_spray_mean_value_changed,  1, 0 );
4487         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4488         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4489         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4490     }
4492     {
4493         /* Standard_deviation */
4494         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4495         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4496         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4497                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4498                                                               "/tools/spray/standard_deviation", 70,
4499                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4500                                                               1, 100, 1.0, 10.0,
4501                                                               labels, values, G_N_ELEMENTS(labels),
4502                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4503         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4504         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4505         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4506     }
4508     /* Mode */
4509     {
4510         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4512         GtkTreeIter iter;
4513         gtk_list_store_append( model, &iter );
4514         gtk_list_store_set( model, &iter,
4515                             0, _("Spray with copies"),
4516                             1, _("Spray copies of the initial selection"),
4517                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4518                             -1 );
4520         gtk_list_store_append( model, &iter );
4521         gtk_list_store_set( model, &iter,
4522                             0, _("Spray with clones"),
4523                             1, _("Spray clones of the initial selection"),
4524                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4525                             -1 );
4527         gtk_list_store_append( model, &iter );
4528         gtk_list_store_set( model, &iter,
4529                             0, _("Spray single path"),
4530                             1, _("Spray objects in a single path"),
4531                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4532                             -1 );
4534         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4535         g_object_set( act, "short_label", _("Mode:"), NULL );
4536         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4537         g_object_set_data( holder, "mode_action", act );
4539         ege_select_one_action_set_appearance( act, "full" );
4540         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4541         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4542         ege_select_one_action_set_icon_column( act, 2 );
4543         ege_select_one_action_set_icon_size( act, secondarySize );
4544         ege_select_one_action_set_tooltip_column( act, 1  );
4546         gint mode = prefs->getInt("/tools/spray/mode", 1);
4547         ege_select_one_action_set_active( act, mode );
4548         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4550         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4551     }
4553     {   /* Population */
4554         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4555         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4556         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4557                                                               _("Amount"), _("Amount:"),
4558                                                               _("Adjusts the number of items sprayed per clic."),
4559                                                               "/tools/spray/population", 70,
4560                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4561                                                               1, 100, 1.0, 10.0,
4562                                                               labels, values, G_N_ELEMENTS(labels),
4563                                                               sp_spray_population_value_changed,  1, 0 );
4564         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4565         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4566         g_object_set_data( holder, "spray_population", eact );
4567     }
4569     /* Use Pressure button */
4570     {
4571         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4572                                                       _("Pressure"),
4573                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4574                                                       "use_pressure",
4575                                                       Inkscape::ICON_SIZE_DECORATION );
4576         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4577         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4578         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4579     }
4581     {   /* Rotation */
4582         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4583         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4584         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4585                                                               _("Rotation"), _("Rotation:"),
4586                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4587                                                               "/tools/spray/rotation_variation", 0,
4588                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4589                                                               0, 100, 1.0, 10.0,
4590                                                               labels, values, G_N_ELEMENTS(labels),
4591                                                               sp_spray_rotation_value_changed,  1, 0 );
4592         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4593         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4594         g_object_set_data( holder, "spray_rotation", eact );
4595     }
4597     {   /* Scale */
4598         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4599         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4600         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4601                                                               _("Scale"), _("Scale:"),
4602                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4603                                                               "/tools/spray/scale_variation", 0,
4604                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4605                                                               0, 100, 1.0, 10.0,
4606                                                               labels, values, G_N_ELEMENTS(labels),
4607                                                               sp_spray_scale_value_changed,  1, 0 );
4608         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4609         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4610         g_object_set_data( holder, "spray_scale", eact );
4611     }
4618 //########################
4619 //##     Calligraphy    ##
4620 //########################
4621 static void update_presets_list (GObject *tbl)
4623     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4624     if (g_object_get_data(tbl, "presets_blocked"))
4625         return;
4627     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4628     if (!sel) {
4629         // WTF!? This will cause a segfault if ever reached
4630         //ege_select_one_action_set_active(sel, 0);
4631         return;
4632     }
4634     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4636     int ege_index = 1;
4637     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4638         bool match = true;
4640         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4641         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4642             Glib::ustring entry_name = j->getEntryName();
4643             if (entry_name == "id" || entry_name == "name") continue;
4645             void *widget = g_object_get_data(tbl, entry_name.data());
4646             if (widget) {
4647                 if (GTK_IS_ADJUSTMENT(widget)) {
4648                     double v = j->getDouble();
4649                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4650                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4651                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4652                         match = false;
4653                         break;
4654                     }
4655                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4656                     bool v = j->getBool();
4657                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4658                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4659                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4660                         match = false;
4661                         break;
4662                     }
4663                 }
4664             }
4665         }
4667         if (match) {
4668             // newly added item is at the same index as the
4669             // save command, so we need to change twice for it to take effect
4670             ege_select_one_action_set_active(sel, 0);
4671             ege_select_one_action_set_active(sel, ege_index); // one-based index
4672             return;
4673         }
4674     }
4676     // no match found
4677     ege_select_one_action_set_active(sel, 0);
4680 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4682     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4683     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4684     update_presets_list(tbl);
4687 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4689     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4690     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4691     update_presets_list(tbl);
4694 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4696     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4697     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4698     update_presets_list(tbl);
4701 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4703     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4704     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4705     update_presets_list(tbl);
4708 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4710     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4711     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4712     update_presets_list(tbl);
4715 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4717     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4718     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4719     update_presets_list(tbl);
4722 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4724     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4725     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4726     update_presets_list(tbl);
4729 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4731     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4732     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4733     update_presets_list(tbl);
4736 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4738     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4739     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4740     update_presets_list(tbl);
4743 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4745     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4746     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4747     update_presets_list(tbl);
4750 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4752     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4753     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4754     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4755     update_presets_list(tbl);
4756     if (calligraphy_angle )
4757         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4761 static gchar const *const widget_names[] = {
4762     "width",
4763     "mass",
4764     "wiggle",
4765     "angle",
4766     "thinning",
4767     "tremor",
4768     "flatness",
4769     "cap_rounding",
4770     "usepressure",
4771     "tracebackground",
4772     "usetilt"
4773 };
4776 static void sp_dcc_build_presets_list(GObject *tbl)
4778     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4780     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4781     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4782     gtk_list_store_clear (model);
4784     {
4785         GtkTreeIter iter;
4786         gtk_list_store_append( model, &iter );
4787         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4788     }
4790     // iterate over all presets to populate the list
4791     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4792     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4793     int ii=1;
4795     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4796         GtkTreeIter iter;
4797         Glib::ustring preset_name = prefs->getString(*i + "/name");
4798         gtk_list_store_append( model, &iter );
4799         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4800     }
4802     {
4803         GtkTreeIter iter;
4804         gtk_list_store_append( model, &iter );
4805         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4806         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4807     }
4809     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4811     update_presets_list (tbl);
4814 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4816     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4817     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4818     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4819     if (! desktop) return;
4821     if (g_object_get_data(tbl, "presets_blocked"))
4822         return;
4824     CalligraphicProfileRename::show(desktop);
4825     if ( !CalligraphicProfileRename::applied()) {
4826         // dialog cancelled
4827         update_presets_list (tbl);
4828         return;
4829     }
4830     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4832     if (profile_name.empty()) {
4833         // empty name entered
4834         update_presets_list (tbl);
4835         return;
4836     }
4838     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4840     // If there's a preset with the given name, find it and set save_path appropriately
4841     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4842     int total_presets = presets.size();
4843     int new_index = -1;
4844     Glib::ustring save_path; // profile pref path without a trailing slash
4846     int temp_index = 0;
4847     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4848         Glib::ustring name = prefs->getString(*i + "/name");
4849         if (!name.empty() && profile_name == name) {
4850             new_index = temp_index;
4851             save_path = *i;
4852             break;
4853         }
4854     }
4856     if (new_index == -1) {
4857         // no preset with this name, create
4858         new_index = total_presets + 1;
4859         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4860         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4861         g_free(profile_id);
4862     }
4864     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4865         gchar const *const widget_name = widget_names[i];
4866         void *widget = g_object_get_data(tbl, widget_name);
4867         if (widget) {
4868             if (GTK_IS_ADJUSTMENT(widget)) {
4869                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4870                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4871                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4872             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4873                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4874                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4875                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4876             } else {
4877                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4878             }
4879         } else {
4880             g_warning("Bad key when writing preset: %s\n", widget_name);
4881         }
4882     }
4883     prefs->setString(save_path + "/name", profile_name);
4885     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4886     sp_dcc_build_presets_list (tbl);
4890 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4892     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4894     gint preset_index = ege_select_one_action_get_active( act );
4895     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4896     // even when the preset is not changed. It would be good to replace it with something more
4897     // modern. Index 0 means "No preset", so we don't do anything.
4898     if (preset_index == 0) return;
4900     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4902     if (preset_index == save_presets_index) {
4903         // this is the Save command
4904         sp_dcc_save_profile(NULL, tbl);
4905         return;
4906     }
4908     if (g_object_get_data(tbl, "presets_blocked"))
4909         return;
4911     // preset_index is one-based so we subtract 1
4912     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4913     Glib::ustring preset_path = presets.at(preset_index - 1);
4915     if (!preset_path.empty()) {
4916         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
4918         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4920         // Shouldn't this be std::map?
4921         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4922             Glib::ustring entry_name = i->getEntryName();
4923             if (entry_name == "id" || entry_name == "name") continue;
4924             void *widget = g_object_get_data(tbl, entry_name.data());
4925             if (widget) {
4926                 if (GTK_IS_ADJUSTMENT(widget)) {
4927                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4928                     gtk_adjustment_set_value(adj, i->getDouble());
4929                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4930                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4931                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4932                     gtk_toggle_action_set_active(toggle, i->getBool());
4933                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4934                 } else {
4935                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4936                 }
4937             } else {
4938                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4939             }
4940         }
4941         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4942     }
4946 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4948     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4949     {
4950         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4952         EgeAdjustmentAction* calligraphy_angle = 0;
4954         {
4955         /* Width */
4956         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4957         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4958         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4959                                                               _("Pen Width"), _("Width:"),
4960                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4961                                                               "/tools/calligraphic/width", 15,
4962                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4963                                                               1, 100, 1.0, 10.0,
4964                                                               labels, values, G_N_ELEMENTS(labels),
4965                                                               sp_ddc_width_value_changed,  1, 0 );
4966         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4967         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4968         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4969         }
4971         {
4972         /* Thinning */
4973             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4974             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4975         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4976                                                               _("Stroke Thinning"), _("Thinning:"),
4977                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4978                                                               "/tools/calligraphic/thinning", 10,
4979                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4980                                                               -100, 100, 1, 10.0,
4981                                                               labels, values, G_N_ELEMENTS(labels),
4982                                                               sp_ddc_velthin_value_changed, 1, 0);
4983         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4984         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4985         }
4987         {
4988         /* Angle */
4989         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4990         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4991         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4992                                                               _("Pen Angle"), _("Angle:"),
4993                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4994                                                               "/tools/calligraphic/angle", 30,
4995                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4996                                                               -90.0, 90.0, 1.0, 10.0,
4997                                                               labels, values, G_N_ELEMENTS(labels),
4998                                                               sp_ddc_angle_value_changed, 1, 0 );
4999         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5000         g_object_set_data( holder, "angle_action", eact );
5001         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5002         calligraphy_angle = eact;
5003         }
5005         {
5006         /* Fixation */
5007             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5008         gdouble values[] = {0, 20, 40, 60, 90, 100};
5009         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5010                                                               _("Fixation"), _("Fixation:"),
5011                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5012                                                               "/tools/calligraphic/flatness", 90,
5013                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5014                                                               0.0, 100, 1.0, 10.0,
5015                                                               labels, values, G_N_ELEMENTS(labels),
5016                                                               sp_ddc_flatness_value_changed, 1, 0);
5017         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5018         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5019         }
5021         {
5022         /* Cap Rounding */
5023             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5024         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5025         // TRANSLATORS: "cap" means "end" (both start and finish) here
5026         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5027                                                               _("Cap rounding"), _("Caps:"),
5028                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5029                                                               "/tools/calligraphic/cap_rounding", 0.0,
5030                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5031                                                               0.0, 5.0, 0.01, 0.1,
5032                                                               labels, values, G_N_ELEMENTS(labels),
5033                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5034         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5035         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5036         }
5038         {
5039         /* Tremor */
5040             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5041         gdouble values[] = {0, 10, 20, 40, 60, 100};
5042         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5043                                                               _("Stroke Tremor"), _("Tremor:"),
5044                                                               _("Increase to make strokes rugged and trembling"),
5045                                                               "/tools/calligraphic/tremor", 0.0,
5046                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5047                                                               0.0, 100, 1, 10.0,
5048                                                               labels, values, G_N_ELEMENTS(labels),
5049                                                               sp_ddc_tremor_value_changed, 1, 0);
5051         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5052         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5053         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5054         }
5056         {
5057         /* Wiggle */
5058         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5059         gdouble values[] = {0, 20, 40, 60, 100};
5060         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5061                                                               _("Pen Wiggle"), _("Wiggle:"),
5062                                                               _("Increase to make the pen waver and wiggle"),
5063                                                               "/tools/calligraphic/wiggle", 0.0,
5064                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5065                                                               0.0, 100, 1, 10.0,
5066                                                               labels, values, G_N_ELEMENTS(labels),
5067                                                               sp_ddc_wiggle_value_changed, 1, 0);
5068         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5069         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5070         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5071         }
5073         {
5074         /* Mass */
5075             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5076         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5077         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5078                                                               _("Pen Mass"), _("Mass:"),
5079                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5080                                                               "/tools/calligraphic/mass", 2.0,
5081                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5082                                                               0.0, 100, 1, 10.0,
5083                                                               labels, values, G_N_ELEMENTS(labels),
5084                                                               sp_ddc_mass_value_changed, 1, 0);
5085         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5086         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5087         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5088         }
5091         /* Trace Background button */
5092         {
5093             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5094                                                           _("Trace Background"),
5095                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5096                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5097                                                           Inkscape::ICON_SIZE_DECORATION );
5098             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5099             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5100             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5101             g_object_set_data( holder, "tracebackground", act );
5102         }
5104         /* Use Pressure button */
5105         {
5106             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5107                                                           _("Pressure"),
5108                                                           _("Use the pressure of the input device to alter the width of the pen"),
5109                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5110                                                           Inkscape::ICON_SIZE_DECORATION );
5111             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5112             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5113             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5114             g_object_set_data( holder, "usepressure", act );
5115         }
5117         /* Use Tilt button */
5118         {
5119             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5120                                                           _("Tilt"),
5121                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5122                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5123                                                           Inkscape::ICON_SIZE_DECORATION );
5124             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5125             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5126             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5127             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5128             g_object_set_data( holder, "usetilt", act );
5129         }
5131         /*calligraphic profile */
5132         {
5133             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5134             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5135             ege_select_one_action_set_appearance (act1, "compact");
5136             g_object_set_data (holder, "profile_selector", act1 );
5138             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5140             sp_dcc_build_presets_list (holder);
5142             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5143             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5144         }
5145     }
5149 //########################
5150 //##    Circle / Arc    ##
5151 //########################
5153 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5155     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5156     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5158     if (v1 == 0 && v2 == 0) {
5159         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5160             gtk_action_set_sensitive( ocb, FALSE );
5161             gtk_action_set_sensitive( make_whole, FALSE );
5162         }
5163     } else {
5164         gtk_action_set_sensitive( ocb, TRUE );
5165         gtk_action_set_sensitive( make_whole, TRUE );
5166     }
5169 static void
5170 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5172     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5174     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5175         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5176         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5177     }
5179     // quit if run by the attr_changed listener
5180     if (g_object_get_data( tbl, "freeze" )) {
5181         return;
5182     }
5184     // in turn, prevent listener from responding
5185     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5187     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5189     bool modmade = false;
5190     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5191          items != NULL;
5192          items = items->next)
5193     {
5194         SPItem *item = SP_ITEM(items->data);
5196         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5198             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5199             SPArc *arc = SP_ARC(item);
5201             if (!strcmp(value_name, "start"))
5202                 ge->start = (adj->value * M_PI)/ 180;
5203             else
5204                 ge->end = (adj->value * M_PI)/ 180;
5206             sp_genericellipse_normalize(ge);
5207             ((SPObject *)arc)->updateRepr();
5208             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5210             modmade = true;
5211         }
5212     }
5214     g_free(namespaced_name);
5216     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5218     sp_arctb_sensitivize( tbl, adj->value, other->value );
5220     if (modmade) {
5221         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5222                                    _("Arc: Change start/end"));
5223     }
5225     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5229 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5231     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5234 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5236     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5240 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5242     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5243     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5244         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5245         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5246     }
5248     // quit if run by the attr_changed listener
5249     if (g_object_get_data( tbl, "freeze" )) {
5250         return;
5251     }
5253     // in turn, prevent listener from responding
5254     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5256     bool modmade = false;
5258     if ( ege_select_one_action_get_active(act) != 0 ) {
5259         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5260              items != NULL;
5261              items = items->next)
5262         {
5263             if (SP_IS_ARC((SPItem *) items->data)) {
5264                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5265                 repr->setAttribute("sodipodi:open", "true");
5266                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5267                 modmade = true;
5268             }
5269         }
5270     } else {
5271         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5272              items != NULL;
5273              items = items->next)
5274         {
5275             if (SP_IS_ARC((SPItem *) items->data))    {
5276                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5277                 repr->setAttribute("sodipodi:open", NULL);
5278                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5279                 modmade = true;
5280             }
5281         }
5282     }
5284     if (modmade) {
5285         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5286                                    _("Arc: Change open/closed"));
5287     }
5289     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5292 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5294     GtkAdjustment *adj;
5295     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5296     gtk_adjustment_set_value(adj, 0.0);
5297     gtk_adjustment_value_changed(adj);
5299     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5300     gtk_adjustment_set_value(adj, 0.0);
5301     gtk_adjustment_value_changed(adj);
5303     spinbutton_defocus( GTK_OBJECT(obj) );
5306 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5307                                       gchar const */*old_value*/, gchar const */*new_value*/,
5308                                       bool /*is_interactive*/, gpointer data)
5310     GObject *tbl = G_OBJECT(data);
5312     // quit if run by the _changed callbacks
5313     if (g_object_get_data( tbl, "freeze" )) {
5314         return;
5315     }
5317     // in turn, prevent callbacks from responding
5318     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5320     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5321     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5323     GtkAdjustment *adj1,*adj2;
5324     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5325     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5326     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5327     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5329     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5331     char const *openstr = NULL;
5332     openstr = repr->attribute("sodipodi:open");
5333     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5335     if (openstr) {
5336         ege_select_one_action_set_active( ocb, 1 );
5337     } else {
5338         ege_select_one_action_set_active( ocb, 0 );
5339     }
5341     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5344 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5345     NULL, /* child_added */
5346     NULL, /* child_removed */
5347     arc_tb_event_attr_changed,
5348     NULL, /* content_changed */
5349     NULL  /* order_changed */
5350 };
5353 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5355     int n_selected = 0;
5356     Inkscape::XML::Node *repr = NULL;
5358     purge_repr_listener( tbl, tbl );
5360     for (GSList const *items = selection->itemList();
5361          items != NULL;
5362          items = items->next)
5363     {
5364         if (SP_IS_ARC((SPItem *) items->data)) {
5365             n_selected++;
5366             repr = SP_OBJECT_REPR((SPItem *) items->data);
5367         }
5368     }
5370     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5372     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5373     if (n_selected == 0) {
5374         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5375     } else if (n_selected == 1) {
5376         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5377         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5379         if (repr) {
5380             g_object_set_data( tbl, "repr", repr );
5381             Inkscape::GC::anchor(repr);
5382             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5383             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5384         }
5385     } else {
5386         // FIXME: implement averaging of all parameters for multiple selected
5387         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5388         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5389         sp_arctb_sensitivize( tbl, 1, 0 );
5390     }
5394 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5396     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5398     EgeAdjustmentAction* eact = 0;
5399     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5402     {
5403         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5404         ege_output_action_set_use_markup( act, TRUE );
5405         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5406         g_object_set_data( holder, "mode_action", act );
5407     }
5409     /* Start */
5410     {
5411         eact = create_adjustment_action( "ArcStartAction",
5412                                          _("Start"), _("Start:"),
5413                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5414                                          "/tools/shapes/arc/start", 0.0,
5415                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5416                                          -360.0, 360.0, 1.0, 10.0,
5417                                          0, 0, 0,
5418                                          sp_arctb_start_value_changed);
5419         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5420     }
5422     /* End */
5423     {
5424         eact = create_adjustment_action( "ArcEndAction",
5425                                          _("End"), _("End:"),
5426                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5427                                          "/tools/shapes/arc/end", 0.0,
5428                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5429                                          -360.0, 360.0, 1.0, 10.0,
5430                                          0, 0, 0,
5431                                          sp_arctb_end_value_changed);
5432         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5433     }
5435     /* Segments / Pie checkbox */
5436     {
5437         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5439         GtkTreeIter iter;
5440         gtk_list_store_append( model, &iter );
5441         gtk_list_store_set( model, &iter,
5442                             0, _("Closed arc"),
5443                             1, _("Switch to segment (closed shape with two radii)"),
5444                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5445                             -1 );
5447         gtk_list_store_append( model, &iter );
5448         gtk_list_store_set( model, &iter,
5449                             0, _("Open Arc"),
5450                             1, _("Switch to arc (unclosed shape)"),
5451                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5452                             -1 );
5454         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5455         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5456         g_object_set_data( holder, "open_action", act );
5458         ege_select_one_action_set_appearance( act, "full" );
5459         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5460         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5461         ege_select_one_action_set_icon_column( act, 2 );
5462         ege_select_one_action_set_icon_size( act, secondarySize );
5463         ege_select_one_action_set_tooltip_column( act, 1  );
5465         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5466         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5467         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5468     }
5470     /* Make Whole */
5471     {
5472         InkAction* inky = ink_action_new( "ArcResetAction",
5473                                           _("Make whole"),
5474                                           _("Make the shape a whole ellipse, not arc or segment"),
5475                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5476                                           secondarySize );
5477         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5478         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5479         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5480         g_object_set_data( holder, "make_whole", inky );
5481     }
5483     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5484     // sensitivize make whole and open checkbox
5485     {
5486         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5487         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5488         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5489     }
5492     sigc::connection *connection = new sigc::connection(
5493         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5494         );
5495     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5496     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5502 // toggle button callbacks and updaters
5504 //########################
5505 //##      Dropper       ##
5506 //########################
5508 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5509     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5510     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5511     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5512     if ( set_action ) {
5513         if ( gtk_toggle_action_get_active( act ) ) {
5514             gtk_action_set_sensitive( set_action, TRUE );
5515         } else {
5516             gtk_action_set_sensitive( set_action, FALSE );
5517         }
5518     }
5520     spinbutton_defocus(GTK_OBJECT(tbl));
5523 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5524     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5525     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5526     spinbutton_defocus(GTK_OBJECT(tbl));
5530 /**
5531  * Dropper auxiliary toolbar construction and setup.
5532  *
5533  * TODO: Would like to add swatch of current color.
5534  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5535  *       can drag and drop places. Will provide a nice mixing palette.
5536  */
5537 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5539     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5540     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5542     {
5543         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5544         ege_output_action_set_use_markup( act, TRUE );
5545         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5546     }
5548     {
5549         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5550                                                       _("Pick opacity"),
5551                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5552                                                       NULL,
5553                                                       Inkscape::ICON_SIZE_DECORATION );
5554         g_object_set( act, "short_label", _("Pick"), NULL );
5555         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5556         g_object_set_data( holder, "pick_action", act );
5557         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5558         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5559     }
5561     {
5562         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5563                                                       _("Assign opacity"),
5564                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5565                                                       NULL,
5566                                                       Inkscape::ICON_SIZE_DECORATION );
5567         g_object_set( act, "short_label", _("Assign"), NULL );
5568         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5569         g_object_set_data( holder, "set_action", act );
5570         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5571         // make sure it's disabled if we're not picking alpha
5572         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5573         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5574     }
5578 //########################
5579 //##      LPETool       ##
5580 //########################
5582 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5584 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5585 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5587     using namespace Inkscape::LivePathEffect;
5589     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5590     SPEventContext *ec = desktop->event_context;
5591     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5592         return;
5593     }
5595     // only take action if run by the attr_changed listener
5596     if (!g_object_get_data(tbl, "freeze")) {
5597         // in turn, prevent listener from responding
5598         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5600         gint mode = ege_select_one_action_get_active(act);
5601         EffectType type = lpesubtools[mode].type;
5603         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5604         bool success = lpetool_try_construction(lc, type);
5605         if (success) {
5606             // since the construction was already performed, we set the state back to inactive
5607             ege_select_one_action_set_active(act, 0);
5608             mode = 0;
5609         } else {
5610             // switch to the chosen subtool
5611             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5612         }
5614         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5615             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5616             prefs->setInt( "/tools/lpetool/mode", mode );
5617         }
5619         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5620     }
5623 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5625     SPEventContext *ec = selection->desktop()->event_context;
5626     if (!SP_IS_LPETOOL_CONTEXT(ec))
5627         return;
5629     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5632 void
5633 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5635     using namespace Inkscape::LivePathEffect;
5636     SPEventContext *ec = selection->desktop()->event_context;
5637     if (!SP_IS_LPETOOL_CONTEXT(ec))
5638         return;
5639     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5641     lpetool_delete_measuring_items(lc);
5642     lpetool_create_measuring_items(lc, selection);
5644     // activate line segment combo box if a single item with LPELineSegment is selected
5645     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5646     SPItem *item = selection->singleItem();
5647     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5648         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5649         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5650         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5651             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5652             g_object_set_data(tbl, "currentlpe", lpe);
5653             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5654             gtk_action_set_sensitive(w, TRUE);
5655             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5656         } else {
5657             g_object_set_data(tbl, "currentlpe", NULL);
5658             g_object_set_data(tbl, "currentlpeitem", NULL);
5659             gtk_action_set_sensitive(w, FALSE);
5660         }
5661     } else {
5662         g_object_set_data(tbl, "currentlpe", NULL);
5663         g_object_set_data(tbl, "currentlpeitem", NULL);
5664         gtk_action_set_sensitive(w, FALSE);
5665     }
5668 static void
5669 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5670     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5671     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5673     bool show = gtk_toggle_action_get_active( act );
5674     prefs->setBool("/tools/lpetool/show_bbox",  show);
5676     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5677         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5678         lpetool_context_reset_limiting_bbox(lc);
5679     }
5682 static void
5683 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5684     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5685     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5686         return;
5688     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5689     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5690     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5691         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5692         bool show = gtk_toggle_action_get_active( act );
5693         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5694         lpetool_show_measuring_info(lc, show);
5695         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5696     }
5699 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5700     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5701     SPUnit const *unit = tracker->getActiveUnit();
5702     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5703     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5705     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5706     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5707         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5708         lpetool_delete_measuring_items(lc);
5709         lpetool_create_measuring_items(lc);
5710     }
5713 static void
5714 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5715     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5716     Inkscape::Selection *selection = desktop->selection;
5718     Geom::OptRect bbox = selection->bounds();
5720     if (bbox) {
5721         Geom::Point A(bbox->min());
5722         Geom::Point B(bbox->max());
5724         A *= desktop->doc2dt();
5725         B *= desktop->doc2dt();
5727         // TODO: should we provide a way to store points in prefs?
5728         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5729         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5730         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5731         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5732         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5734         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5735     }
5737     gtk_toggle_action_set_active(act, false);
5740 static void
5741 sp_line_segment_build_list(GObject *tbl)
5743     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5745     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5746     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5747     gtk_list_store_clear (model);
5749     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5750     {
5751         GtkTreeIter iter;
5752         gtk_list_store_append( model, &iter );
5753         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5754         gtk_list_store_append( model, &iter );
5755         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5756         gtk_list_store_append( model, &iter );
5757         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5758         gtk_list_store_append( model, &iter );
5759         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5760     }
5762     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5765 static void
5766 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5767     using namespace Inkscape::LivePathEffect;
5769     // quit if run by the attr_changed listener
5770     if (g_object_get_data(tbl, "freeze")) {
5771         return;
5772     }
5774     // in turn, prevent listener from responding
5775     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5777     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5778     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5779     if (lpeitem) {
5780         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5781         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5782         sp_lpe_item_update_patheffect(lpeitem, true, true);
5783     }
5785     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5788 static void
5789 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5790     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5792     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5793         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5794     }
5795     gtk_toggle_action_set_active(act, false);
5798 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5800     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5801     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5802     g_object_set_data(holder, "tracker", tracker);
5803     SPUnit const *unit = tracker->getActiveUnit();
5805     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5806     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5808     /** Automatically create a list of LPEs that get added to the toolbar **/
5809     {
5810         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5812         GtkTreeIter iter;
5814         // the first toggle button represents the state that no subtool is active (remove this when
5815         // this can be modeled by EgeSelectOneAction or some other action)
5816         gtk_list_store_append( model, &iter );
5817         gtk_list_store_set( model, &iter,
5818                             0, _("All inactive"),
5819                             1, _("No geometric tool is active"),
5820                             2, "draw-geometry-inactive",
5821                             -1 );
5823         Inkscape::LivePathEffect::EffectType type;
5824         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5825             type =  lpesubtools[i].type;
5826             gtk_list_store_append( model, &iter );
5827             gtk_list_store_set( model, &iter,
5828                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5829                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5830                                 2, lpesubtools[i].icon_name,
5831                                 -1 );
5832         }
5834         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5835         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5836         g_object_set_data( holder, "lpetool_mode_action", act );
5838         ege_select_one_action_set_appearance( act, "full" );
5839         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5840         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5841         ege_select_one_action_set_icon_column( act, 2 );
5842         ege_select_one_action_set_tooltip_column( act, 1  );
5844         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5845         ege_select_one_action_set_active( act, lpeToolMode );
5846         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5847     }
5849     /* Show limiting bounding box */
5850     {
5851         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5852                                                       _("Show limiting bounding box"),
5853                                                       _("Show bounding box (used to cut infinite lines)"),
5854                                                       "show-bounding-box",
5855                                                       Inkscape::ICON_SIZE_DECORATION );
5856         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5857         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5858         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5859     }
5861     /* Set limiting bounding box to bbox of current selection */
5862     {
5863         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5864                                                       _("Get limiting bounding box from selection"),
5865                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5866                                                       "draw-geometry-set-bounding-box",
5867                                                       Inkscape::ICON_SIZE_DECORATION );
5868         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5869         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5870         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5871     }
5874     /* Combo box to choose line segment type */
5875     {
5876         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5877         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5878         ege_select_one_action_set_appearance (act, "compact");
5879         g_object_set_data (holder, "lpetool_line_segment_action", act );
5881         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5883         sp_line_segment_build_list (holder);
5885         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5886         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5887         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5888     }
5890     /* Display measuring info for selected items */
5891     {
5892         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5893                                                       _("Display measuring info"),
5894                                                       _("Display measuring info for selected items"),
5895                                                       "draw-geometry-show-measuring-info",
5896                                                       Inkscape::ICON_SIZE_DECORATION );
5897         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5898         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5899         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5900     }
5902     // add the units menu
5903     {
5904         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5905         gtk_action_group_add_action( mainActions, act );
5906         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5907         g_object_set_data(holder, "lpetool_units_action", act);
5908         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5909     }
5911     /* Open LPE dialog (to adapt parameters numerically) */
5912     {
5913         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5914                                                       _("Open LPE dialog"),
5915                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5916                                                       "dialog-geometry",
5917                                                       Inkscape::ICON_SIZE_DECORATION );
5918         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5919         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5920         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5921     }
5923     //watch selection
5924     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5926     sigc::connection *c_selection_modified =
5927         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5928                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5929     pool->add_connection ("selection-modified", c_selection_modified);
5931     sigc::connection *c_selection_changed =
5932         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5933                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5934     pool->add_connection ("selection-changed", c_selection_changed);
5937 //########################
5938 //##       Eraser       ##
5939 //########################
5941 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5943     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5944     prefs->setDouble( "/tools/eraser/width", adj->value );
5945     update_presets_list(tbl);
5948 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5950     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5951     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5952     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5953         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5954         prefs->setBool( "/tools/eraser/mode", eraserMode );
5955     }
5957     // only take action if run by the attr_changed listener
5958     if (!g_object_get_data( tbl, "freeze" )) {
5959         // in turn, prevent listener from responding
5960         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5962         if ( eraserMode != 0 ) {
5963         } else {
5964         }
5965         // TODO finish implementation
5967         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5968     }
5971 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5973     {
5974         /* Width */
5975         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5976         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5977         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5978                                                               _("Pen Width"), _("Width:"),
5979                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5980                                                               "/tools/eraser/width", 15,
5981                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5982                                                               1, 100, 1.0, 10.0,
5983                                                               labels, values, G_N_ELEMENTS(labels),
5984                                                               sp_erc_width_value_changed, 1, 0);
5985         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5986         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5987         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5988     }
5990     {
5991         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5993         GtkTreeIter iter;
5994         gtk_list_store_append( model, &iter );
5995         gtk_list_store_set( model, &iter,
5996                             0, _("Delete"),
5997                             1, _("Delete objects touched by the eraser"),
5998                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5999                             -1 );
6001         gtk_list_store_append( model, &iter );
6002         gtk_list_store_set( model, &iter,
6003                             0, _("Cut"),
6004                             1, _("Cut out from objects"),
6005                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6006                             -1 );
6008         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6009         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6010         g_object_set_data( holder, "eraser_mode_action", act );
6012         ege_select_one_action_set_appearance( act, "full" );
6013         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6014         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6015         ege_select_one_action_set_icon_column( act, 2 );
6016         ege_select_one_action_set_tooltip_column( act, 1  );
6018         /// @todo Convert to boolean?
6019         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6020         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6021         ege_select_one_action_set_active( act, eraserMode );
6022         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6023     }
6027 //########################
6028 //##    Text Toolbox    ##
6029 //########################
6030 /*
6031 static void
6032 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6034     //Call back for letter sizing spinbutton
6037 static void
6038 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6040     //Call back for line height spinbutton
6043 static void
6044 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6046     //Call back for horizontal kerning spinbutton
6049 static void
6050 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6052     //Call back for vertical kerning spinbutton
6055 static void
6056 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6058     //Call back for letter rotation spinbutton
6059 }*/
6061 namespace {
6063 void
6064 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6066     // quit if run by the _changed callbacks
6067     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6068         return;
6069     }
6071     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6073     SPStyle *query =
6074         sp_style_new (SP_ACTIVE_DOCUMENT);
6076     int result_family =
6077         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6079     int result_style =
6080         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6082     int result_numbers =
6083         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6085     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6087     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6088     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6089         // there are no texts in selection, read from prefs
6091         sp_style_read_from_prefs(query, "/tools/text");
6093         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6094             // do not reset the toolbar style from prefs if we already did it last time
6095             sp_style_unref(query);
6096             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6097             return;
6098         }
6099         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6100     } else {
6101         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6102     }
6104     if (query->text)
6105     {
6106         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6107             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6108             gtk_entry_set_text (GTK_ENTRY (entry), "");
6110         } else if (query->text->font_specification.value || query->text->font_family.value) {
6112             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6113             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6115             // Get the font that corresponds
6116             Glib::ustring familyName;
6118             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6119             if (font) {
6120                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6121                 font->Unref();
6122                 font = NULL;
6123             }
6125             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6127             Gtk::TreeIter iter;
6128             try {
6129                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6130                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6131                 iter = model->get_iter(path);
6132             } catch (...) {
6133                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6134                 sp_style_unref(query);
6135                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6136                 return;
6137             }
6139             combo->set_active (iter);
6140         }
6142         //Size
6143         {
6144             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6145             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6146             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6147             g_free(str);
6148         }
6150         //Anchor
6151         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6152         {
6153             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6154             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6155             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6156             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6157         }
6158         else
6159         {
6160             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6161             {
6162                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6163                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6164                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6165                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6166             }
6167             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6168             {
6169                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6170                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6171                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6172                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6173             }
6174             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6175             {
6176                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6177                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6178                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6179                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6180             }
6181         }
6183         //Style
6184         {
6185             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6187             gboolean active = gtk_toggle_button_get_active (button);
6188             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));
6190             if (active != check)
6191             {
6192                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6193                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6194                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6195             }
6196         }
6198         {
6199             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6201             gboolean active = gtk_toggle_button_get_active (button);
6202             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6204             if (active != check)
6205             {
6206                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6207                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6208                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6209             }
6210         }
6212         //Orientation
6213         //locking both buttons, changing one affect all group (both)
6214         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6215         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6217         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6218         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6220         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6221         {
6222             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6223         }
6224         else
6225         {
6226             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6227         }
6228         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6229         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6230     }
6232     sp_style_unref(query);
6234     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6237 void
6238 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6240     sp_text_toolbox_selection_changed (selection, tbl);
6243 void
6244 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6246     sp_text_toolbox_selection_changed (NULL, tbl);
6249 void
6250 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6251                                 GObject             *tbl)
6253     // quit if run by the _changed callbacks
6254     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6255         return;
6256     }
6258     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6260     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6261     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6262     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6264     //g_print ("family changed to: %s\n", family);
6266     SPStyle *query =
6267         sp_style_new (SP_ACTIVE_DOCUMENT);
6269     int result_fontspec =
6270         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6272     SPCSSAttr *css = sp_repr_css_attr_new ();
6274     // First try to get the font spec from the stored value
6275     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6277     if (fontSpec.empty()) {
6278         // Construct a new font specification if it does not yet exist
6279         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6280         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6281         fontFromStyle->Unref();
6282     }
6284     if (!fontSpec.empty()) {
6286         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6288         if (!newFontSpec.empty()) {
6290             if (fontSpec != newFontSpec) {
6292                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6294                 if (font) {
6295                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6297                     // Set all the these just in case they were altered when finding the best
6298                     // match for the new family and old style...
6300                     gchar c[256];
6302                     font->Family(c, 256);
6304                     sp_repr_css_set_property (css, "font-family", c);
6306                     font->Attribute( "weight", c, 256);
6307                     sp_repr_css_set_property (css, "font-weight", c);
6309                     font->Attribute("style", c, 256);
6310                     sp_repr_css_set_property (css, "font-style", c);
6312                     font->Attribute("stretch", c, 256);
6313                     sp_repr_css_set_property (css, "font-stretch", c);
6315                     font->Attribute("variant", c, 256);
6316                     sp_repr_css_set_property (css, "font-variant", c);
6318                     font->Unref();
6319                 }
6320             }
6322         } else {
6323             // If the old font on selection (or default) was not existing on the system,
6324             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6325             // setting the family reported by the family chooser.
6327             //g_print ("fallback setting family: %s\n", family);
6328             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6329             sp_repr_css_set_property (css, "font-family", family);
6330         }
6331     }
6333     // If querying returned nothing, set the default style of the tool (for new texts)
6334     if (result_fontspec == QUERY_STYLE_NOTHING)
6335     {
6336         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6337         prefs->mergeStyle("/tools/text/style", css);
6338         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6339     }
6340     else
6341     {
6342         sp_desktop_set_style (desktop, css, true, true);
6343     }
6345     sp_style_unref(query);
6347     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6348                                    _("Text: Change font family"));
6349     sp_repr_css_attr_unref (css);
6351     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6353     // unfreeze
6354     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6356     // focus to canvas
6357     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6361 void
6362 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6363                                    gpointer          data)
6365     if (g_object_get_data (G_OBJECT (button), "block")) return;
6366     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6367     int prop = GPOINTER_TO_INT(data);
6369     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6371     // move the x of all texts to preserve the same bbox
6372     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6373     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6374         if (SP_IS_TEXT((SPItem *) items->data)) {
6375             SPItem *item = SP_ITEM(items->data);
6377             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6378             // below, variable names suggest horizontal move, but we check the writing direction
6379             // and move in the corresponding axis
6380             int axis;
6381             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6382                 axis = NR::X;
6383             } else {
6384                 axis = NR::Y;
6385             }
6387             Geom::OptRect bbox
6388                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6389             if (!bbox)
6390                 continue;
6391             double width = bbox->dimensions()[axis];
6392             // If you want to align within some frame, other than the text's own bbox, calculate
6393             // the left and right (or top and bottom for tb text) slacks of the text inside that
6394             // frame (currently unused)
6395             double left_slack = 0;
6396             double right_slack = 0;
6397             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6398             double move = 0;
6399             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6400                 switch (prop) {
6401                     case 0:
6402                         move = -left_slack;
6403                         break;
6404                     case 1:
6405                         move = width/2 + (right_slack - left_slack)/2;
6406                         break;
6407                     case 2:
6408                         move = width + right_slack;
6409                         break;
6410                 }
6411             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6412                 switch (prop) {
6413                     case 0:
6414                         move = -width/2 - left_slack;
6415                         break;
6416                     case 1:
6417                         move = (right_slack - left_slack)/2;
6418                         break;
6419                     case 2:
6420                         move = width/2 + right_slack;
6421                         break;
6422                 }
6423             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6424                 switch (prop) {
6425                     case 0:
6426                         move = -width - left_slack;
6427                         break;
6428                     case 1:
6429                         move = -width/2 + (right_slack - left_slack)/2;
6430                         break;
6431                     case 2:
6432                         move = right_slack;
6433                         break;
6434                 }
6435             }
6436             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6437             if (axis == NR::X) {
6438                 XY = XY + Geom::Point (move, 0);
6439             } else {
6440                 XY = XY + Geom::Point (0, move);
6441             }
6442             SP_TEXT(item)->attributes.setFirstXY(XY);
6443             SP_OBJECT(item)->updateRepr();
6444             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6445         }
6446     }
6448     SPCSSAttr *css = sp_repr_css_attr_new ();
6449     switch (prop)
6450     {
6451         case 0:
6452         {
6453             sp_repr_css_set_property (css, "text-anchor", "start");
6454             sp_repr_css_set_property (css, "text-align", "start");
6455             break;
6456         }
6457         case 1:
6458         {
6459             sp_repr_css_set_property (css, "text-anchor", "middle");
6460             sp_repr_css_set_property (css, "text-align", "center");
6461             break;
6462         }
6464         case 2:
6465         {
6466             sp_repr_css_set_property (css, "text-anchor", "end");
6467             sp_repr_css_set_property (css, "text-align", "end");
6468             break;
6469         }
6471         case 3:
6472         {
6473             sp_repr_css_set_property (css, "text-anchor", "start");
6474             sp_repr_css_set_property (css, "text-align", "justify");
6475             break;
6476         }
6477     }
6479     SPStyle *query =
6480         sp_style_new (SP_ACTIVE_DOCUMENT);
6481     int result_numbers =
6482         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6484     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6485     if (result_numbers == QUERY_STYLE_NOTHING)
6486     {
6487         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6488         prefs->mergeStyle("/tools/text/style", css);
6489     }
6491     sp_style_unref(query);
6493     sp_desktop_set_style (desktop, css, true, true);
6494     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6495                                    _("Text: Change alignment"));
6496     sp_repr_css_attr_unref (css);
6498     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6501 void
6502 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6503                                gpointer          data)
6505     if (g_object_get_data (G_OBJECT (button), "block")) return;
6507     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6508     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6509     int          prop       = GPOINTER_TO_INT(data);
6510     bool         active     = gtk_toggle_button_get_active (button);
6512     SPStyle *query =
6513         sp_style_new (SP_ACTIVE_DOCUMENT);
6515     int result_fontspec =
6516         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6518     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6519     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6520     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6522     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6523     Glib::ustring newFontSpec = "";
6525     if (fontSpec.empty()) {
6526         // Construct a new font specification if it does not yet exist
6527         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6528         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6529         fontFromStyle->Unref();
6530     }
6532     bool nochange = true;
6533     switch (prop)
6534     {
6535         case 0:
6536         {
6537             if (!fontSpec.empty()) {
6538                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6539                 if (!newFontSpec.empty()) {
6540                     // Don't even set the bold if the font didn't exist on the system
6541                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6542                     nochange = false;
6543                 }
6544             }
6545             // set or reset the button according
6546             if(nochange) {
6547                 gboolean check = gtk_toggle_button_get_active (button);
6549                 if (active != check)
6550                 {
6551                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6552                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6553                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6554                 }
6555             }
6557             break;
6558         }
6560         case 1:
6561         {
6562             if (!fontSpec.empty()) {
6563                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6564                 if (!newFontSpec.empty()) {
6565                     // Don't even set the italic if the font didn't exist on the system
6566                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6567                     nochange = false;
6568                 }
6569             }
6570             if(nochange) {
6571                 gboolean check = gtk_toggle_button_get_active (button);
6573                 if (active != check)
6574                 {
6575                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6576                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6577                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6578                 }
6579             }
6580             break;
6581         }
6582     }
6584     if (!newFontSpec.empty()) {
6585         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6586     }
6588     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6589     if (result_fontspec == QUERY_STYLE_NOTHING)
6590     {
6591         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6592         prefs->mergeStyle("/tools/text/style", css);
6593     }
6595     sp_style_unref(query);
6597     sp_desktop_set_style (desktop, css, true, true);
6598     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6599                                    _("Text: Change font style"));
6600     sp_repr_css_attr_unref (css);
6602     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6605 void
6606 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6607                                      gpointer         data)
6609     if (g_object_get_data (G_OBJECT (button), "block")) {
6610         return;
6611     }
6613     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6614     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6615     int          prop       = GPOINTER_TO_INT(data);
6617     switch (prop)
6618     {
6619         case 0:
6620         {
6621             sp_repr_css_set_property (css, "writing-mode", "lr");
6622             break;
6623         }
6625         case 1:
6626         {
6627             sp_repr_css_set_property (css, "writing-mode", "tb");
6628             break;
6629         }
6630     }
6632     SPStyle *query =
6633         sp_style_new (SP_ACTIVE_DOCUMENT);
6634     int result_numbers =
6635         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6637     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6638     if (result_numbers == QUERY_STYLE_NOTHING)
6639     {
6640         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6641         prefs->mergeStyle("/tools/text/style", css);
6642     }
6644     sp_desktop_set_style (desktop, css, true, true);
6645     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6646                                    _("Text: Change orientation"));
6647     sp_repr_css_attr_unref (css);
6649     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6652 gboolean
6653 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6655     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6656     if (!desktop) return FALSE;
6658     switch (get_group0_keyval (event)) {
6659         case GDK_KP_Enter: // chosen
6660         case GDK_Return:
6661             // unfreeze and update, which will defocus
6662             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6663             sp_text_toolbox_family_changed (NULL, tbl);
6664             return TRUE; // I consumed the event
6665             break;
6666         case GDK_Escape:
6667             // defocus
6668             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6669             return TRUE; // I consumed the event
6670             break;
6671     }
6672     return FALSE;
6675 gboolean
6676 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6678     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6679     if (!desktop) return FALSE;
6681     switch (get_group0_keyval (event)) {
6682         case GDK_KP_Enter:
6683         case GDK_Return:
6684         case GDK_Escape: // defocus
6685             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6686             return TRUE; // I consumed the event
6687             break;
6688         case GDK_w:
6689         case GDK_W:
6690             if (event->state & GDK_CONTROL_MASK) {
6691                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6692                 return TRUE; // I consumed the event
6693             }
6694             break;
6695     }
6696     return FALSE;
6700 void
6701 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6702                                GObject     *tbl)
6704      // quit if run by the _changed callbacks
6705     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6706         return;
6707     }
6709     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6711    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6713     // If this is not from selecting a size in the list (in which case get_active will give the
6714     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6715     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6716     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6717    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6718         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6719         return;
6720    }
6722     gdouble value = -1;
6723     {
6724         gchar *endptr;
6725         gchar *const text = gtk_combo_box_get_active_text(cbox);
6726         if (text) {
6727             value = g_strtod(text, &endptr);
6728             if (endptr == text) {  // Conversion failed, non-numeric input.
6729                 value = -1;
6730             }
6731             g_free(text);
6732         }
6733     }
6734     if (value <= 0) {
6735         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6736         return; // could not parse value
6737     }
6739     SPCSSAttr *css = sp_repr_css_attr_new ();
6740     Inkscape::CSSOStringStream osfs;
6741     osfs << value;
6742     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6744     SPStyle *query =
6745         sp_style_new (SP_ACTIVE_DOCUMENT);
6746     int result_numbers =
6747         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6749     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6750     if (result_numbers == QUERY_STYLE_NOTHING)
6751     {
6752         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6753         prefs->mergeStyle("/tools/text/style", css);
6754     }
6756     sp_style_unref(query);
6758     sp_desktop_set_style (desktop, css, true, true);
6759     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6760                                    _("Text: Change font size"));
6761     sp_repr_css_attr_unref (css);
6763     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6765     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6768 gboolean
6769 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6771     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6772     if (!desktop) return FALSE;
6774     if (!g_object_get_data (tbl, "esc-pressed")) {
6775         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6776         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6777         sp_text_toolbox_size_changed (cbox, tbl);
6778         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6779     }
6780     return FALSE; // I consumed the event
6784 gboolean
6785 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6787     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6788     if (!desktop) return FALSE;
6790     switch (get_group0_keyval (event)) {
6791         case GDK_Escape: // defocus
6792             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6793             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6794             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6795             return TRUE; // I consumed the event
6796             break;
6797         case GDK_Return: // defocus
6798         case GDK_KP_Enter:
6799             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6800             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6801             sp_text_toolbox_size_changed (cbox, tbl);
6802             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6803             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6804             return TRUE; // I consumed the event
6805             break;
6806     }
6807     return FALSE;
6810 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6811 // does not work!
6812 gboolean
6813 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6814                                  GdkEventFocus    */*event*/,
6815                                  GObject          *tbl)
6817     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6818     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6819     return FALSE;
6822 gboolean
6823 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6824                                  GdkEventFocus    */*event*/,
6825                                  GObject          *tbl)
6827     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6828     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6829     return FALSE;
6832 void
6833 cell_data_func  (GtkCellLayout */*cell_layout*/,
6834                  GtkCellRenderer   *cell,
6835                  GtkTreeModel      *tree_model,
6836                  GtkTreeIter       *iter,
6837                  gpointer           /*data*/)
6839     gchar *family;
6840     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6841     gchar *const family_escaped = g_markup_escape_text(family, -1);
6843     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6844     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6845     if (show_sample) {
6847         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6848         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6850     std::stringstream markup;
6851     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6852            << family_escaped << "'>" << sample_escaped << "</span>";
6853     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6855         g_free(sample_escaped);
6856     } else {
6857         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6858     }
6860     g_free(family);
6861     g_free(family_escaped);
6864 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6865                                                 GtkTreeModel       *model,
6866                                                 GtkTreeIter        *iter,
6867                                                 GObject            *tbl)
6869     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6870     // press Enter again after choosing a completion)
6871     gchar *family = 0;
6872     gtk_tree_model_get(model, iter, 0, &family, -1);
6874     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6875     gtk_entry_set_text (GTK_ENTRY (entry), family);
6877     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6878     sp_text_toolbox_family_changed (NULL, tbl);
6879     return TRUE;
6883 static void
6884 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6885     GtkEntry *entry;
6886     GtkEntryCompletion *completion;
6887     GtkTreeModel *model;
6889     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6890     completion = gtk_entry_completion_new();
6891     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6892     gtk_entry_completion_set_model(completion, model);
6893     gtk_entry_completion_set_text_column(completion, 0);
6894     gtk_entry_completion_set_inline_completion(completion, FALSE);
6895     gtk_entry_completion_set_inline_selection(completion, FALSE);
6896     gtk_entry_completion_set_popup_completion(completion, TRUE);
6897     gtk_entry_set_completion(entry, completion);
6899     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6901     g_object_unref(completion);
6904 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6905                                       void */*property*/,
6906                                       GObject *tbl)
6908   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6910   gboolean shown;
6911   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6912   if (shown) {
6913          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6914          //g_print("POP: notify: SHOWN\n");
6915   } else {
6916          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6918          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6919          // find out if the drop down was closed by Enter and if so, manually update (only
6920          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6921          GdkEvent *ev = gtk_get_current_event();
6922          if (ev) {
6923              //g_print ("ev type: %d\n", ev->type);
6924              if (ev->type == GDK_KEY_PRESS) {
6925                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6926                      case GDK_KP_Enter: // chosen
6927                      case GDK_Return:
6928                      {
6929                          // make sure the chosen one is inserted into the entry
6930                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6931                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6932                          GtkTreeIter iter;
6933                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6934                          if (has_active) {
6935                              gchar *family;
6936                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6937                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6938                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6939                          }
6941                          // update
6942                          sp_text_toolbox_family_changed (NULL, tbl);
6943                          break;
6944                      }
6945                  }
6946              }
6947          }
6949          // regardless of whether we updated, defocus the widget
6950          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6951          if (desktop)
6952              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6953          //g_print("POP: notify: HIDDEN\n");
6954   }
6957 GtkWidget*
6958 sp_text_toolbox_new (SPDesktop *desktop)
6960     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6961     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6963     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6964     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6966     GtkTooltips *tt = gtk_tooltips_new();
6968     ////////////Family
6969     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6970     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6972     gtk_rc_parse_string (
6973        "style \"dropdown-as-list-style\"\n"
6974        "{\n"
6975        "    GtkComboBox::appears-as-list = 1\n"
6976        "}\n"
6977        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6978     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6979     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6981     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6983     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6985     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6986     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6988     // expand the field a bit so as to view more of the previews in the drop-down
6989     GtkRequisition req;
6990     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6991     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6993     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6994     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6996     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6997     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6998              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6999     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7000     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7001     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7003     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7004     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7006     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7007     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7008     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7009     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7011     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7012     GtkWidget *box = gtk_event_box_new ();
7013     gtk_container_add (GTK_CONTAINER (box), image);
7014     gtk_toolbar_append_widget( tbl, box, "", "");
7015     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7016     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7017     gtk_widget_hide (GTK_WIDGET (box));
7018     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7020     ////////////Size
7021     gchar const *const sizes[] = {
7022         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7023         "16", "18", "20", "22", "24", "28",
7024         "32", "36", "40", "48", "56", "64", "72", "144"
7025     };
7027     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7028     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7029         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7030     }
7031     gtk_widget_set_size_request (cbox, 80, -1);
7032     gtk_toolbar_append_widget( tbl, cbox, "", "");
7033     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7034     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7035     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7036     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7038     ////////////Text anchor
7039     GtkWidget *group   = gtk_radio_button_new (NULL);
7040     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7041     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7043     // left
7044     GtkWidget *rbutton = group;
7045     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7046     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7047     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7049     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7050     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7051     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7052     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7054     // center
7055     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7056     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7057     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7058     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7060     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7061     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7062     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7063     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7065     // right
7066     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7067     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7068     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7069     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7071     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7072     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7073     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7074     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7076     // fill
7077     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7078     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7079     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7080     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7082     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7083     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7084     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7085     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7087     gtk_toolbar_append_widget( tbl, row, "", "");
7089     //spacer
7090     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7092     ////////////Text style
7093     row = gtk_hbox_new (FALSE, 4);
7095     // bold
7096     rbutton = gtk_toggle_button_new ();
7097     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7098     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7099     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7100     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7102     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7103     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7104     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7106     // italic
7107     rbutton = gtk_toggle_button_new ();
7108     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7109     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7110     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7111     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7113     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7114     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7115     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7117     gtk_toolbar_append_widget( tbl, row, "", "");
7119     //spacer
7120     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7122     // Text orientation
7123     group   = gtk_radio_button_new (NULL);
7124     row     = gtk_hbox_new (FALSE, 4);
7125     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7127     // horizontal
7128     rbutton = group;
7129     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7130     gtk_container_add           (GTK_CONTAINER (rbutton),
7131                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7132     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7133     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7135     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7136     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7137     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7139     // vertical
7140     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7141     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7142     gtk_container_add           (GTK_CONTAINER (rbutton),
7143                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7144     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7145     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7147     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7148     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7149     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7150     gtk_toolbar_append_widget( tbl, row, "", "" );
7153     //watch selection
7154     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7156     sigc::connection *c_selection_changed =
7157         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7158                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7159     pool->add_connection ("selection-changed", c_selection_changed);
7161     sigc::connection *c_selection_modified =
7162         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7163                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7164     pool->add_connection ("selection-modified", c_selection_modified);
7166     sigc::connection *c_subselection_changed =
7167         new sigc::connection (desktop->connectToolSubselectionChanged
7168                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7169     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7171     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7174     gtk_widget_show_all( GTK_WIDGET(tbl) );
7176     return GTK_WIDGET(tbl);
7177 } // end of sp_text_toolbox_new()
7179 }//<unnamed> namespace
7182 //#########################
7183 //##      Connector      ##
7184 //#########################
7186 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7188     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7189     prefs->setBool("/tools/connector/mode",
7190                 gtk_toggle_action_get_active( act ));
7193 static void sp_connector_path_set_avoid(void)
7195     cc_selection_set_avoid(true);
7199 static void sp_connector_path_set_ignore(void)
7201     cc_selection_set_avoid(false);
7204 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7206     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7207     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7208     SPDocument *doc = sp_desktop_document(desktop);
7210     if (!sp_document_get_undo_sensitive(doc))
7211     {
7212         return;
7213     }
7216     // quit if run by the _changed callbacks
7217     if (g_object_get_data( tbl, "freeze" )) {
7218         return;
7219     }
7221     // in turn, prevent callbacks from responding
7222     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7224     bool is_orthog = gtk_toggle_action_get_active( act );
7225     gchar orthog_str[] = "orthogonal";
7226     gchar polyline_str[] = "polyline";
7227     gchar *value = is_orthog ? orthog_str : polyline_str ;
7229     bool modmade = false;
7230     GSList *l = (GSList *) selection->itemList();
7231     while (l) {
7232         SPItem *item = (SPItem *) l->data;
7234         if (cc_item_is_connector(item)) {
7235             sp_object_setAttribute(item, "inkscape:connector-type",
7236                     value, false);
7237             item->avoidRef->handleSettingChange();
7238             modmade = true;
7239         }
7240         l = l->next;
7241     }
7243     if (!modmade)
7244     {
7245         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7246         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7247     }
7249     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7250             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7252     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7255 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7257     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7258     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7259     SPDocument *doc = sp_desktop_document(desktop);
7261     if (!sp_document_get_undo_sensitive(doc))
7262     {
7263         return;
7264     }
7267     // quit if run by the _changed callbacks
7268     if (g_object_get_data( tbl, "freeze" )) {
7269         return;
7270     }
7272     // in turn, prevent callbacks from responding
7273     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7275     gdouble newValue = gtk_adjustment_get_value(adj);
7276     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7277     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7279     bool modmade = false;
7280     GSList *l = (GSList *) selection->itemList();
7281     while (l) {
7282         SPItem *item = (SPItem *) l->data;
7284         if (cc_item_is_connector(item)) {
7285             sp_object_setAttribute(item, "inkscape:connector-curvature",
7286                     value, false);
7287             item->avoidRef->handleSettingChange();
7288             modmade = true;
7289         }
7290         l = l->next;
7291     }
7293     if (!modmade)
7294     {
7295         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7296         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7297     }
7299     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7300             _("Change connector curvature"));
7302     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7306 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7308     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7309     SPDocument *doc = sp_desktop_document(desktop);
7311     if (!sp_document_get_undo_sensitive(doc))
7312     {
7313         return;
7314     }
7316     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7318     if ( !repr->attribute("inkscape:connector-spacing") &&
7319             ( adj->value == defaultConnSpacing )) {
7320         // Don't need to update the repr if the attribute doesn't
7321         // exist and it is being set to the default value -- as will
7322         // happen at startup.
7323         return;
7324     }
7326     // quit if run by the attr_changed listener
7327     if (g_object_get_data( tbl, "freeze" )) {
7328         return;
7329     }
7331     // in turn, prevent listener from responding
7332     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7334     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7335     SP_OBJECT(desktop->namedview)->updateRepr();
7337     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7338     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7339         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7340         Geom::Matrix m = Geom::identity();
7341         avoid_item_move(&m, item);
7342     }
7344     if (items) {
7345         g_slist_free(items);
7346     }
7348     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7349             _("Change connector spacing"));
7351     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7354 static void sp_connector_graph_layout(void)
7356     if (!SP_ACTIVE_DESKTOP) return;
7357     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7359     // hack for clones, see comment in align-and-distribute.cpp
7360     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7361     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7363     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7365     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7367     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7370 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7372     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7373     prefs->setBool("/tools/connector/directedlayout",
7374                 gtk_toggle_action_get_active( act ));
7377 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7379     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7380     prefs->setBool("/tools/connector/avoidoverlaplayout",
7381                 gtk_toggle_action_get_active( act ));
7385 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7387     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7388     prefs->setDouble("/tools/connector/length", adj->value);
7391 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7392                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7393                                             bool /*is_interactive*/, gpointer data)
7395     GtkWidget *tbl = GTK_WIDGET(data);
7397     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7398         return;
7399     }
7400     if (strcmp(name, "inkscape:connector-spacing") == 0)
7401     {
7402         GtkAdjustment *adj = (GtkAdjustment*)
7403                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7404         gdouble spacing = defaultConnSpacing;
7405         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7407         gtk_adjustment_set_value(adj, spacing);
7408         gtk_adjustment_value_changed(adj);
7409     }
7411     spinbutton_defocus(GTK_OBJECT(tbl));
7414 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7416     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7417     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7419     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7420         cc_create_connection_point(cc);
7423 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7425     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7426     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7428     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7429         cc_remove_connection_point(cc);
7432 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7433     NULL, /* child_added */
7434     NULL, /* child_removed */
7435     connector_tb_event_attr_changed,
7436     NULL, /* content_changed */
7437     NULL  /* order_changed */
7438 };
7440 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7442     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7443     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7444     SPItem *item = selection->singleItem();
7445     if (SP_IS_PATH(item))
7446     {
7447         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7448         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7449         gtk_toggle_action_set_active(act, is_orthog);
7450         gtk_adjustment_set_value(adj, curvature);
7451     }
7455 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7457     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7458     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
7460     // Editing mode toggle button
7461     {
7462         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7463                                                       _("EditMode"),
7464                                                       _("Switch between connection point editing and connector drawing mode"),
7465                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7466                                                       Inkscape::ICON_SIZE_DECORATION );
7467         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7469         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7470         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7471         g_object_set_data( holder, "mode", act );
7472         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7473     }
7476     {
7477         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7478                                           _("Avoid"),
7479                                           _("Make connectors avoid selected objects"),
7480                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7481                                           secondarySize );
7482         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7483         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7484     }
7486     {
7487         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7488                                           _("Ignore"),
7489                                           _("Make connectors ignore selected objects"),
7490                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7491                                           secondarySize );
7492         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7493         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7494     }
7496     // Orthogonal connectors toggle button
7497     {
7498         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7499                                                       _("Orthogonal"),
7500                                                       _("Make connector orthogonal or polyline"),
7501                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7502                                                       Inkscape::ICON_SIZE_DECORATION );
7503         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7505         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7506         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7507         g_object_set_data( holder, "orthogonal", act );
7508         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7509     }
7511     EgeAdjustmentAction* eact = 0;
7512     // Curvature spinbox
7513     eact = create_adjustment_action( "ConnectorCurvatureAction",
7514                                     _("Connector Curvature"), _("Curvature:"),
7515                                     _("The amount of connectors curvature"),
7516                                     "/tools/connector/curvature", defaultConnCurvature,
7517                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7518                                     0, 100, 1.0, 10.0,
7519                                     0, 0, 0,
7520                                     connector_curvature_changed, 1, 0 );
7521     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7523     // Spacing spinbox
7524     eact = create_adjustment_action( "ConnectorSpacingAction",
7525                                     _("Connector Spacing"), _("Spacing:"),
7526                                     _("The amount of space left around objects by auto-routing connectors"),
7527                                     "/tools/connector/spacing", defaultConnSpacing,
7528                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7529                                     0, 100, 1.0, 10.0,
7530                                     0, 0, 0,
7531                                     connector_spacing_changed, 1, 0 );
7532     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7534     // Graph (connector network) layout
7535     {
7536         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7537                                           _("Graph"),
7538                                           _("Nicely arrange selected connector network"),
7539                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7540                                           secondarySize );
7541         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7542         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7543     }
7545     // Default connector length spinbox
7546     eact = create_adjustment_action( "ConnectorLengthAction",
7547                                      _("Connector Length"), _("Length:"),
7548                                      _("Ideal length for connectors when layout is applied"),
7549                                      "/tools/connector/length", 100,
7550                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7551                                      10, 1000, 10.0, 100.0,
7552                                      0, 0, 0,
7553                                      connector_length_changed, 1, 0 );
7554     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7557     // Directed edges toggle button
7558     {
7559         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7560                                                       _("Downwards"),
7561                                                       _("Make connectors with end-markers (arrows) point downwards"),
7562                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7563                                                       Inkscape::ICON_SIZE_DECORATION );
7564         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7566         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7567         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7569         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7570         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7571         );
7572     }
7574     // Avoid overlaps toggle button
7575     {
7576         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7577                                                       _("Remove overlaps"),
7578                                                       _("Do not allow overlapping shapes"),
7579                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7580                                                       Inkscape::ICON_SIZE_DECORATION );
7581         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7583         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7584         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7586         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7587     }
7590     // New connection point button
7591     {
7592         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7593                                           _("New connection point"),
7594                                           _("Add a new connection point to the currently selected item"),
7595                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7596                                           secondarySize );
7597         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7598         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7599     }
7601     // Remove selected connection point button
7603     {
7604         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7605                                           _("Remove connection point"),
7606                                           _("Remove the currently selected connection point"),
7607                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7608                                           secondarySize );
7609         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7610         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7611     }
7614     // Code to watch for changes to the connector-spacing attribute in
7615     // the XML.
7616     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7617     g_assert(repr != NULL);
7619     purge_repr_listener( holder, holder );
7621     if (repr) {
7622         g_object_set_data( holder, "repr", repr );
7623         Inkscape::GC::anchor(repr);
7624         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7625         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7626     }
7627 } // end of sp_connector_toolbox_prep()
7630 //#########################
7631 //##     Paintbucket     ##
7632 //#########################
7634 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7636     gint channels = ege_select_one_action_get_active( act );
7637     flood_channels_set_channels( channels );
7640 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7642     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7643     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7646 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7648     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7649     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7652 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7654     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7655     SPUnit const *unit = tracker->getActiveUnit();
7656     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7658     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7659     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7662 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7664     // FIXME: make defaults settable via Inkscape Options
7665     struct KeyValue {
7666         char const *key;
7667         double value;
7668     } const key_values[] = {
7669         {"threshold", 15},
7670         {"offset", 0.0}
7671     };
7673     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7674         KeyValue const &kv = key_values[i];
7675         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7676         if ( adj ) {
7677             gtk_adjustment_set_value(adj, kv.value);
7678         }
7679     }
7681     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7682     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7683     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7684     ege_select_one_action_set_active( autogap_action, 0 );
7687 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7689     EgeAdjustmentAction* eact = 0;
7690     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7692     {
7693         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7695         GList* items = 0;
7696         gint count = 0;
7697         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7698         {
7699             GtkTreeIter iter;
7700             gtk_list_store_append( model, &iter );
7701             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7702             count++;
7703         }
7704         g_list_free( items );
7705         items = 0;
7706         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7707         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7708         ege_select_one_action_set_appearance( act1, "compact" );
7709         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7710         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7711         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7712         g_object_set_data( holder, "channels_action", act1 );
7713     }
7715     // Spacing spinbox
7716     {
7717         eact = create_adjustment_action(
7718             "ThresholdAction",
7719             _("Fill Threshold"), _("Threshold:"),
7720             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7721             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7722             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7723             0, 0, 0,
7724             paintbucket_threshold_changed, 1, 0 );
7726         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7727         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7728     }
7730     // Create the units menu.
7731     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7732     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7733     if (!stored_unit.empty())
7734         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7735     g_object_set_data( holder, "tracker", tracker );
7736     {
7737         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7738         gtk_action_group_add_action( mainActions, act );
7739     }
7741     // Offset spinbox
7742     {
7743         eact = create_adjustment_action(
7744             "OffsetAction",
7745             _("Grow/shrink by"), _("Grow/shrink by:"),
7746             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7747             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7748             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7749             0, 0, 0,
7750             paintbucket_offset_changed, 1, 2);
7751         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7753         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7754     }
7756     /* Auto Gap */
7757     {
7758         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7760         GList* items = 0;
7761         gint count = 0;
7762         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7763         {
7764             GtkTreeIter iter;
7765             gtk_list_store_append( model, &iter );
7766             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7767             count++;
7768         }
7769         g_list_free( items );
7770         items = 0;
7771         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7772         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7773         ege_select_one_action_set_appearance( act2, "compact" );
7774         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7775         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7776         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7777         g_object_set_data( holder, "autogap_action", act2 );
7778     }
7780     /* Reset */
7781     {
7782         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7783                                           _("Defaults"),
7784                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7785                                           GTK_STOCK_CLEAR );
7786         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7787         gtk_action_group_add_action( mainActions, act );
7788         gtk_action_set_sensitive( act, TRUE );
7789     }
7793 /*
7794   Local Variables:
7795   mode:c++
7796   c-file-style:"stroustrup"
7797   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7798   indent-tabs-mode:nil
7799   fill-column:99
7800   End:
7801 */
7802 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :