Code

b945765db14e852399f30234b8c895e6ea6f327e
[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@scislac.com>
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>
36 #include "widgets/button.h"
37 #include "widgets/widget-sizes.h"
38 #include "widgets/spw-utilities.h"
39 #include "widgets/spinbutton-events.h"
40 #include "dialogs/text-edit.h"
41 #include "dialogs/dialog-events.h"
43 #include "ui/widget/style-swatch.h"
45 #include "verbs.h"
46 #include "sp-namedview.h"
47 #include "desktop.h"
48 #include "desktop-handles.h"
49 #include "xml/repr.h"
50 #include "xml/node-event-vector.h"
51 #include "xml/attribute-record.h"
52 #include <glibmm/i18n.h>
53 #include "helper/unit-menu.h"
54 #include "helper/units.h"
55 #include "live_effects/effect.h"
57 #include "inkscape.h"
58 #include "conn-avoid-ref.h"
61 #include "select-toolbar.h"
62 #include "gradient-toolbar.h"
64 #include "connector-context.h"
65 #include "node-context.h"
66 #include "pen-context.h"
67 #include "lpe-tool-context.h"
68 #include "live_effects/lpe-line_segment.h"
69 #include "shape-editor.h"
70 #include "tweak-context.h"
71 #include "sp-rect.h"
72 #include "box3d.h"
73 #include "box3d-context.h"
74 #include "sp-star.h"
75 #include "sp-spiral.h"
76 #include "sp-ellipse.h"
77 #include "sp-text.h"
78 #include "sp-flowtext.h"
79 #include "sp-clippath.h"
80 #include "sp-mask.h"
81 #include "style.h"
82 #include "tools-switch.h"
83 #include "selection.h"
84 #include "selection-chemistry.h"
85 #include "document-private.h"
86 #include "desktop-style.h"
87 #include "../libnrtype/font-lister.h"
88 #include "../libnrtype/font-instance.h"
89 #include "../connection-pool.h"
90 #include "../preferences.h"
91 #include "../inkscape-stock.h"
92 #include "icon.h"
93 #include "graphlayout/graphlayout.h"
94 #include "interface.h"
95 #include "shortcuts.h"
97 #include "mod360.h"
99 #include "toolbox.h"
101 #include "flood-context.h"
103 #include "ink-action.h"
104 #include "ege-adjustment-action.h"
105 #include "ege-output-action.h"
106 #include "ege-select-one-action.h"
107 #include "helper/unit-tracker.h"
108 #include "live_effects/lpe-angle_bisector.h"
110 #include "svg/css-ostringstream.h"
112 #include "widgets/calligraphic-profile-rename.h"
114 using Inkscape::UnitTracker;
116 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
117 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
119 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
132 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
140 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
141     static Inkscape::IconSize sizeChoices[] = {
142         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
143         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
144         Inkscape::ICON_SIZE_MENU
145     };
146     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
147     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
148     return sizeChoices[index];
151 static struct {
152     gchar const *type_name;
153     gchar const *data_name;
154     sp_verb_t verb;
155     sp_verb_t doubleclick_verb;
156 } const tools[] = {
157     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
158     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
159     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
160     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
161     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
162     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
163     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
164     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
165     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
166     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
167     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
168     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
169     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
170     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
171     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
172     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
173     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
174     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
175     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
176     { NULL, NULL, 0, 0 }
177 };
179 static struct {
180     gchar const *type_name;
181     gchar const *data_name;
182     GtkWidget *(*create_func)(SPDesktop *desktop);
183     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
184     gchar const *ui_name;
185     gint swatch_verb_id;
186     gchar const *swatch_tool;
187     gchar const *swatch_tip;
188 } const aux_toolboxes[] = {
189     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
190       SP_VERB_INVALID, 0, 0},
191     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
192       SP_VERB_INVALID, 0, 0},
193     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
194       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
195     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
196       SP_VERB_INVALID, 0, 0},
197     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
198       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
199     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
200       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
201     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
202       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
203     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
204       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
205     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
206       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
207     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
208       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
209     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
210       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
211     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
212       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
213     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
214       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
215     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
216       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
217     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
218       SP_VERB_INVALID, 0, 0},
219     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
220       SP_VERB_INVALID, 0, 0},
221     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
222       SP_VERB_INVALID, 0, 0},
223     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
224       SP_VERB_INVALID, 0, 0},
225     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
226       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
227     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
228 };
230 #define TOOLBAR_SLIDER_HINT "full"
232 static gchar const * ui_descr =
233         "<ui>"
234         "  <toolbar name='SelectToolbar'>"
235         "    <toolitem action='EditSelectAll' />"
236         "    <toolitem action='EditSelectAllInAllLayers' />"
237         "    <toolitem action='EditDeselect' />"
238         "    <separator />"
239         "    <toolitem action='ObjectRotate90CCW' />"
240         "    <toolitem action='ObjectRotate90' />"
241         "    <toolitem action='ObjectFlipHorizontally' />"
242         "    <toolitem action='ObjectFlipVertically' />"
243         "    <separator />"
244         "    <toolitem action='SelectionToBack' />"
245         "    <toolitem action='SelectionLower' />"
246         "    <toolitem action='SelectionRaise' />"
247         "    <toolitem action='SelectionToFront' />"
248         "    <separator />"
249         "    <toolitem action='XAction' />"
250         "    <toolitem action='YAction' />"
251         "    <toolitem action='WidthAction' />"
252         "    <toolitem action='LockAction' />"
253         "    <toolitem action='HeightAction' />"
254         "    <toolitem action='UnitsAction' />"
255         "    <separator />"
256         "    <toolitem action='transform_affect_label' />"
257         "    <toolitem action='transform_stroke' />"
258         "    <toolitem action='transform_corners' />"
259         "    <toolitem action='transform_gradient' />"
260         "    <toolitem action='transform_pattern' />"
261         "  </toolbar>"
263         "  <toolbar name='NodeToolbar'>"
264         "    <toolitem action='NodeInsertAction' />"
265         "    <toolitem action='NodeDeleteAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeJoinAction' />"
268         "    <toolitem action='NodeBreakAction' />"
269         "    <separator />"
270         "    <toolitem action='NodeJoinSegmentAction' />"
271         "    <toolitem action='NodeDeleteSegmentAction' />"
272         "    <separator />"
273         "    <toolitem action='NodeCuspAction' />"
274         "    <toolitem action='NodeSmoothAction' />"
275         "    <toolitem action='NodeSymmetricAction' />"
276         "    <toolitem action='NodeAutoAction' />"
277         "    <separator />"
278         "    <toolitem action='NodeLineAction' />"
279         "    <toolitem action='NodeCurveAction' />"
280         "    <separator />"
281         "    <toolitem action='ObjectToPath' />"
282         "    <toolitem action='StrokeToPath' />"
283         "    <separator />"
284         "    <toolitem action='NodeXAction' />"
285         "    <toolitem action='NodeYAction' />"
286         "    <toolitem action='NodeUnitsAction' />"
287         "    <separator />"
288         "    <toolitem action='ObjectEditClipPathAction' />"
289         "    <toolitem action='ObjectEditMaskPathAction' />"
290         "    <toolitem action='EditNextLPEParameterAction' />"
291         "    <separator />"
292         "    <toolitem action='NodesShowHandlesAction' />"
293         "    <toolitem action='NodesShowHelperpath' />"
294         "  </toolbar>"
296         "  <toolbar name='TweakToolbar'>"
297         "    <toolitem action='TweakWidthAction' />"
298         "    <separator />"
299         "    <toolitem action='TweakForceAction' />"
300         "    <toolitem action='TweakPressureAction' />"
301         "    <separator />"
302         "    <toolitem action='TweakModeAction' />"
303         "    <separator />"
304         "    <toolitem action='TweakFidelityAction' />"
305         "    <separator />"
306         "    <toolitem action='TweakChannelsLabel' />"
307         "    <toolitem action='TweakDoH' />"
308         "    <toolitem action='TweakDoS' />"
309         "    <toolitem action='TweakDoL' />"
310         "    <toolitem action='TweakDoO' />"
311         "  </toolbar>"
313         "  <toolbar name='ZoomToolbar'>"
314         "    <toolitem action='ZoomIn' />"
315         "    <toolitem action='ZoomOut' />"
316         "    <separator />"
317         "    <toolitem action='Zoom1:0' />"
318         "    <toolitem action='Zoom1:2' />"
319         "    <toolitem action='Zoom2:1' />"
320         "    <separator />"
321         "    <toolitem action='ZoomSelection' />"
322         "    <toolitem action='ZoomDrawing' />"
323         "    <toolitem action='ZoomPage' />"
324         "    <toolitem action='ZoomPageWidth' />"
325         "    <separator />"
326         "    <toolitem action='ZoomPrev' />"
327         "    <toolitem action='ZoomNext' />"
328         "  </toolbar>"
330         "  <toolbar name='StarToolbar'>"
331         "    <separator />"
332         "    <toolitem action='StarStateAction' />"
333         "    <separator />"
334         "    <toolitem action='FlatAction' />"
335         "    <separator />"
336         "    <toolitem action='MagnitudeAction' />"
337         "    <toolitem action='SpokeAction' />"
338         "    <toolitem action='RoundednessAction' />"
339         "    <toolitem action='RandomizationAction' />"
340         "    <separator />"
341         "    <toolitem action='StarResetAction' />"
342         "  </toolbar>"
344         "  <toolbar name='RectToolbar'>"
345         "    <toolitem action='RectStateAction' />"
346         "    <toolitem action='RectWidthAction' />"
347         "    <toolitem action='RectHeightAction' />"
348         "    <toolitem action='RadiusXAction' />"
349         "    <toolitem action='RadiusYAction' />"
350         "    <toolitem action='RectUnitsAction' />"
351         "    <separator />"
352         "    <toolitem action='RectResetAction' />"
353         "  </toolbar>"
355         "  <toolbar name='3DBoxToolbar'>"
356         "    <toolitem action='3DBoxAngleXAction' />"
357         "    <toolitem action='3DBoxVPXStateAction' />"
358         "    <separator />"
359         "    <toolitem action='3DBoxAngleYAction' />"
360         "    <toolitem action='3DBoxVPYStateAction' />"
361         "    <separator />"
362         "    <toolitem action='3DBoxAngleZAction' />"
363         "    <toolitem action='3DBoxVPZStateAction' />"
364         "  </toolbar>"
366         "  <toolbar name='SpiralToolbar'>"
367         "    <toolitem action='SpiralStateAction' />"
368         "    <toolitem action='SpiralRevolutionAction' />"
369         "    <toolitem action='SpiralExpansionAction' />"
370         "    <toolitem action='SpiralT0Action' />"
371         "    <separator />"
372         "    <toolitem action='SpiralResetAction' />"
373         "  </toolbar>"
375         "  <toolbar name='PenToolbar'>"
376         "    <toolitem action='FreehandModeActionPen' />"
377         "    <separator />"
378         "    <toolitem action='SetPenShapeAction'/>"
379         "  </toolbar>"
381         "  <toolbar name='PencilToolbar'>"
382         "    <toolitem action='FreehandModeActionPencil' />"
383         "    <separator />"
384         "    <toolitem action='PencilToleranceAction' />"
385         "    <separator />"
386         "    <toolitem action='PencilResetAction' />"
387         "    <separator />"
388         "    <toolitem action='SetPencilShapeAction'/>"
389         "  </toolbar>"
391         "  <toolbar name='CalligraphyToolbar'>"
392         "    <separator />"
393         "    <toolitem action='SetProfileAction'/>"
394         "    <separator />"
395         "    <toolitem action='CalligraphyWidthAction' />"
396         "    <toolitem action='PressureAction' />"
397         "    <toolitem action='TraceAction' />"
398         "    <toolitem action='ThinningAction' />"
399         "    <separator />"
400         "    <toolitem action='AngleAction' />"
401         "    <toolitem action='TiltAction' />"
402         "    <toolitem action='FixationAction' />"
403         "    <separator />"
404         "    <toolitem action='CapRoundingAction' />"
405         "    <separator />"
406         "    <toolitem action='TremorAction' />"
407         "    <toolitem action='WiggleAction' />"
408         "    <toolitem action='MassAction' />"
409         "    <separator />"
410         "  </toolbar>"
412         "  <toolbar name='ArcToolbar'>"
413         "    <toolitem action='ArcStateAction' />"
414         "    <separator />"
415         "    <toolitem action='ArcStartAction' />"
416         "    <toolitem action='ArcEndAction' />"
417         "    <separator />"
418         "    <toolitem action='ArcOpenAction' />"
419         "    <separator />"
420         "    <toolitem action='ArcResetAction' />"
421         "    <separator />"
422         "  </toolbar>"
424         "  <toolbar name='PaintbucketToolbar'>"
425         "    <toolitem action='ChannelsAction' />"
426         "    <separator />"
427         "    <toolitem action='ThresholdAction' />"
428         "    <separator />"
429         "    <toolitem action='OffsetAction' />"
430         "    <toolitem action='PaintbucketUnitsAction' />"
431         "    <separator />"
432         "    <toolitem action='AutoGapAction' />"
433         "    <separator />"
434         "    <toolitem action='PaintbucketResetAction' />"
435         "  </toolbar>"
437         "  <toolbar name='EraserToolbar'>"
438         "    <toolitem action='EraserWidthAction' />"
439         "    <separator />"
440         "    <toolitem action='EraserModeAction' />"
441         "  </toolbar>"
443         "  <toolbar name='LPEToolToolbar'>"
444         "    <toolitem action='LPEToolModeAction' />"
445         "    <separator />"
446         "    <toolitem action='LPEShowBBoxAction' />"
447         "    <toolitem action='LPEBBoxFromSelectionAction' />"
448         "    <separator />"
449         "    <toolitem action='LPELineSegmentAction' />"
450         "    <separator />"
451         "    <toolitem action='LPEMeasuringAction' />"
452         "    <toolitem action='LPEToolUnitsAction' />"
453         "    <separator />"
454         "    <toolitem action='LPEOpenLPEDialogAction' />"
455         "  </toolbar>"
457         "  <toolbar name='DropperToolbar'>"
458         "    <toolitem action='DropperOpacityAction' />"
459         "    <toolitem action='DropperPickAlphaAction' />"
460         "    <toolitem action='DropperSetAlphaAction' />"
461         "  </toolbar>"
463         "  <toolbar name='ConnectorToolbar'>"
464         "    <toolitem action='ConnectorAvoidAction' />"
465         "    <toolitem action='ConnectorIgnoreAction' />"
466         "    <toolitem action='ConnectorSpacingAction' />"
467         "    <toolitem action='ConnectorGraphAction' />"
468         "    <toolitem action='ConnectorLengthAction' />"
469         "    <toolitem action='ConnectorDirectedAction' />"
470         "    <toolitem action='ConnectorOverlapAction' />"
471         "  </toolbar>"
473         "</ui>"
476 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
478 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
480 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
481 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
483 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
484 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
486 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
487 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
490 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
491                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
492                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
494 class VerbAction : public Gtk::Action {
495 public:
496     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
498     virtual ~VerbAction();
499     virtual void set_active(bool active = true);
501 protected:
502     virtual Gtk::Widget* create_menu_item_vfunc();
503     virtual Gtk::Widget* create_tool_item_vfunc();
505     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
506     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
508     virtual void on_activate();
510 private:
511     Inkscape::Verb* verb;
512     Inkscape::Verb* verb2;
513     Inkscape::UI::View::View *view;
514     GtkTooltips *tooltips;
515     bool active;
517     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
518 };
521 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
523     Glib::RefPtr<VerbAction> result;
524     SPAction *action = verb->get_action(view);
525     if ( action ) {
526         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
527         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
528     }
530     return result;
533 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
534     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
535     verb(verb),
536     verb2(verb2),
537     view(view),
538     tooltips(tooltips),
539     active(false)
543 VerbAction::~VerbAction()
547 Gtk::Widget* VerbAction::create_menu_item_vfunc()
549 // First call in to get the icon rendered if present in SVG
550     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
551     delete widget;
552     widget = 0;
554     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
555 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
556     return widg;
559 Gtk::Widget* VerbAction::create_tool_item_vfunc()
561 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
562     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
563     GtkWidget* toolbox = 0;
564     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
565                                                                           SP_BUTTON_TYPE_TOGGLE,
566                                                                           verb,
567                                                                           verb2,
568                                                                           view,
569                                                                           tooltips );
570     if ( active ) {
571         sp_button_toggle_set_down( SP_BUTTON(button), active);
572     }
573     gtk_widget_show_all( button );
574     Gtk::Widget* wrapped = Glib::wrap(button);
575     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
576     holder->add(*wrapped);
578 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
579     return holder;
582 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
584 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
585     Gtk::Action::connect_proxy_vfunc(proxy);
588 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
590 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
591     Gtk::Action::disconnect_proxy_vfunc(proxy);
594 void VerbAction::set_active(bool active)
596     this->active = active;
597     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
598     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
599         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
600         if (ti) {
601             // *should* have one child that is the SPButton
602             Gtk::Widget* child = ti->get_child();
603             if ( child && SP_IS_BUTTON(child->gobj()) ) {
604                 SPButton* button = SP_BUTTON(child->gobj());
605                 sp_button_toggle_set_down( button, active );
606             }
607         }
608     }
611 void VerbAction::on_activate()
613     if ( verb ) {
614         SPAction *action = verb->get_action(view);
615         if ( action ) {
616             sp_action_perform(action, 0);
617         }
618     }
621 /* Global text entry widgets necessary for update */
622 /* GtkWidget *dropper_rgb_entry,
623           *dropper_opacity_entry ; */
624 // should be made a private member once this is converted to class
626 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
627     connection->disconnect();
628     delete connection;
631 static void purge_repr_listener( GObject* obj, GObject* tbl )
633     (void)obj;
634     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
635     if (oldrepr) { // remove old listener
636         sp_repr_remove_listener_by_data(oldrepr, tbl);
637         Inkscape::GC::release(oldrepr);
638         oldrepr = 0;
639         g_object_set_data( tbl, "repr", NULL );
640     }
643 GtkWidget *
644 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
645                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
646                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
648     SPAction *action = verb->get_action(view);
649     if (!action) return NULL;
651     SPAction *doubleclick_action;
652     if (doubleclick_verb)
653         doubleclick_action = doubleclick_verb->get_action(view);
654     else
655         doubleclick_action = NULL;
657     /* fixme: Handle sensitive/unsensitive */
658     /* fixme: Implement sp_button_new_from_action */
659     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
660     gtk_widget_show(b);
663     unsigned int shortcut = sp_shortcut_get_primary(verb);
664     if (shortcut) {
665         gchar key[256];
666         sp_ui_shortcut_string(shortcut, key);
667         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
668         if ( t ) {
669             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
670         }
671         g_free(tip);
672     } else {
673         if ( t ) {
674             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
675         }
676     }
678     return b;
682 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
684     SPAction* targetAction = SP_ACTION(user_data);
685     if ( targetAction ) {
686         sp_action_perform( targetAction, NULL );
687     }
690 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
692     if ( data ) {
693         GtkAction* act = GTK_ACTION(data);
694         gtk_action_set_sensitive( act, sensitive );
695     }
698 static SPActionEventVector action_event_vector = {
699     {NULL},
700     NULL,
701     NULL,
702     sp_action_action_set_sensitive,
703     NULL,
704     NULL
705 };
707 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
709     GtkAction* act = 0;
711     SPAction* targetAction = verb->get_action(view);
712     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
713     act = GTK_ACTION(inky);
714     gtk_action_set_sensitive( act, targetAction->sensitive );
716     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
718     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
719     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
721     return act;
724 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
726     Inkscape::UI::View::View *view = desktop;
727     gint verbsToUse[] = {
728         // disabled until we have icons for them:
729         //find
730         //SP_VERB_EDIT_TILE,
731         //SP_VERB_EDIT_UNTILE,
732         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
733         SP_VERB_DIALOG_DISPLAY,
734         SP_VERB_DIALOG_FILL_STROKE,
735         SP_VERB_DIALOG_NAMEDVIEW,
736         SP_VERB_DIALOG_TEXT,
737         SP_VERB_DIALOG_XML_EDITOR,
738         SP_VERB_EDIT_CLONE,
739         SP_VERB_EDIT_COPY,
740         SP_VERB_EDIT_CUT,
741         SP_VERB_EDIT_DUPLICATE,
742         SP_VERB_EDIT_PASTE,
743         SP_VERB_EDIT_REDO,
744         SP_VERB_EDIT_UNDO,
745         SP_VERB_EDIT_UNLINK_CLONE,
746         SP_VERB_FILE_EXPORT,
747         SP_VERB_FILE_IMPORT,
748         SP_VERB_FILE_NEW,
749         SP_VERB_FILE_OPEN,
750         SP_VERB_FILE_PRINT,
751         SP_VERB_FILE_SAVE,
752         SP_VERB_OBJECT_TO_CURVE,
753         SP_VERB_SELECTION_GROUP,
754         SP_VERB_SELECTION_OUTLINE,
755         SP_VERB_SELECTION_UNGROUP,
756         SP_VERB_ZOOM_1_1,
757         SP_VERB_ZOOM_1_2,
758         SP_VERB_ZOOM_2_1,
759         SP_VERB_ZOOM_DRAWING,
760         SP_VERB_ZOOM_IN,
761         SP_VERB_ZOOM_NEXT,
762         SP_VERB_ZOOM_OUT,
763         SP_VERB_ZOOM_PAGE,
764         SP_VERB_ZOOM_PAGE_WIDTH,
765         SP_VERB_ZOOM_PREV,
766         SP_VERB_ZOOM_SELECTION,
767     };
769     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
771     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
772     Glib::RefPtr<Gtk::ActionGroup> mainActions;
773     if ( groups.find(desktop) != groups.end() ) {
774         mainActions = groups[desktop];
775     }
777     if ( !mainActions ) {
778         mainActions = Gtk::ActionGroup::create("main");
779         groups[desktop] = mainActions;
780     }
782     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
783         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
784         if ( verb ) {
785             if (!mainActions->get_action(verb->get_id())) {
786                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
787                 mainActions->add(Glib::wrap(act));
788             }
789         }
790     }
792     if ( !mainActions->get_action("ToolZoom") ) {
793         GtkTooltips *tt = gtk_tooltips_new();
794         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
795             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
796             if ( va ) {
797                 mainActions->add(va);
798                 if ( i == 0 ) {
799                     va->set_active(true);
800                 }
801             }
802         }
803     }
806     return mainActions;
810 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
812     gtk_widget_set_size_request( widget,
813                                  widget->allocation.width,
814                                  widget->allocation.height );
817 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
819     gtk_widget_set_size_request( widget, -1, -1 );
824 GtkWidget *
825 sp_tool_toolbox_new()
827     GtkTooltips *tt = gtk_tooltips_new();
828     GtkWidget* tb = gtk_toolbar_new();
829     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
830     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
832     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
833     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
835     gtk_widget_set_sensitive(tb, FALSE);
837     GtkWidget *hb = gtk_handle_box_new();
838     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
839     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
840     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
842     gtk_container_add(GTK_CONTAINER(hb), tb);
843     gtk_widget_show(GTK_WIDGET(tb));
845     sigc::connection* conn = new sigc::connection;
846     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
848     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
849     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
851     return hb;
854 GtkWidget *
855 sp_aux_toolbox_new()
857     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
859     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
861     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
863     gtk_widget_set_sensitive(tb, FALSE);
865     GtkWidget *hb = gtk_handle_box_new();
866     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
867     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
868     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
870     gtk_container_add(GTK_CONTAINER(hb), tb);
871     gtk_widget_show(GTK_WIDGET(tb));
873     sigc::connection* conn = new sigc::connection;
874     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
876     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
877     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
879     return hb;
882 //####################################
883 //# Commands Bar
884 //####################################
886 GtkWidget *
887 sp_commands_toolbox_new()
889     GtkWidget *tb = gtk_toolbar_new();
891     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
892     gtk_widget_set_sensitive(tb, FALSE);
894     GtkWidget *hb = gtk_handle_box_new();
895     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
896     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
897     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
899     gtk_container_add(GTK_CONTAINER(hb), tb);
900     gtk_widget_show(GTK_WIDGET(tb));
902     sigc::connection* conn = new sigc::connection;
903     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
905     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
906     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
908     return hb;
912 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
913                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
914                                                        Glib::ustring const &path, gdouble def,
915                                                        GtkWidget *focusTarget,
916                                                        GtkWidget *us,
917                                                        GObject *dataKludge,
918                                                        gboolean altx, gchar const *altx_mark,
919                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
920                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
921                                                        void (*callback)(GtkAdjustment *, GObject *),
922                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
924     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
925     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
926                                                              lower, upper, step, page, page ) );
927     if (us) {
928         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
929     }
931     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
933     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
934     if ( shortLabel ) {
935         g_object_set( act, "short_label", shortLabel, NULL );
936     }
938     if ( (descrCount > 0) && descrLabels && descrValues ) {
939         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
940     }
942     if ( focusTarget ) {
943         ege_adjustment_action_set_focuswidget( act, focusTarget );
944     }
946     if ( altx && altx_mark ) {
947         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
948     }
950     if ( dataKludge ) {
951         // Rather lame, but it's the only place where we need to get the entry name
952         // but we don't have an Entry
953         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
954     }
956     // Using a cast just to make sure we pass in the right kind of function pointer
957     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
959     return act;
963 //####################################
964 //# node editing callbacks
965 //####################################
967 /**
968  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
969  */
970 static ShapeEditor *get_current_shape_editor()
972     if (!SP_ACTIVE_DESKTOP) {
973         return NULL;
974     }
976     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
978     if (!SP_IS_NODE_CONTEXT(event_context)) {
979         return NULL;
980     }
982     return SP_NODE_CONTEXT(event_context)->shape_editor;
986 void
987 sp_node_path_edit_add(void)
989     ShapeEditor *shape_editor = get_current_shape_editor();
990     if (shape_editor) shape_editor->add_node();
993 void
994 sp_node_path_edit_delete(void)
996     ShapeEditor *shape_editor = get_current_shape_editor();
997     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1000 void
1001 sp_node_path_edit_delete_segment(void)
1003     ShapeEditor *shape_editor = get_current_shape_editor();
1004     if (shape_editor) shape_editor->delete_segment();
1007 void
1008 sp_node_path_edit_break(void)
1010     ShapeEditor *shape_editor = get_current_shape_editor();
1011     if (shape_editor) shape_editor->break_at_nodes();
1014 void
1015 sp_node_path_edit_join(void)
1017     ShapeEditor *shape_editor = get_current_shape_editor();
1018     if (shape_editor) shape_editor->join_nodes();
1021 void
1022 sp_node_path_edit_join_segment(void)
1024     ShapeEditor *shape_editor = get_current_shape_editor();
1025     if (shape_editor) shape_editor->join_segments();
1028 void
1029 sp_node_path_edit_toline(void)
1031     ShapeEditor *shape_editor = get_current_shape_editor();
1032     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1035 void
1036 sp_node_path_edit_tocurve(void)
1038     ShapeEditor *shape_editor = get_current_shape_editor();
1039     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1042 void
1043 sp_node_path_edit_cusp(void)
1045     ShapeEditor *shape_editor = get_current_shape_editor();
1046     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1049 void
1050 sp_node_path_edit_smooth(void)
1052     ShapeEditor *shape_editor = get_current_shape_editor();
1053     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1056 void
1057 sp_node_path_edit_symmetrical(void)
1059     ShapeEditor *shape_editor = get_current_shape_editor();
1060     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1063 void
1064 sp_node_path_edit_auto(void)
1066     ShapeEditor *shape_editor = get_current_shape_editor();
1067     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1070 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1071     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1072     bool show = gtk_toggle_action_get_active( act );
1073     prefs->setBool("/tools/nodes/show_handles",  show);
1074     ShapeEditor *shape_editor = get_current_shape_editor();
1075     if (shape_editor) shape_editor->show_handles(show);
1078 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1079     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1080     bool show = gtk_toggle_action_get_active( act );
1081     prefs->setBool("/tools/nodes/show_helperpath",  show);
1082     ShapeEditor *shape_editor = get_current_shape_editor();
1083     if (shape_editor) shape_editor->show_helperpath(show);
1086 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1087     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1090 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1091     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1094 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1095     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1098 /* is called when the node selection is modified */
1099 static void
1100 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1102     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1103     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1104     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1105     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1107     // quit if run by the attr_changed listener
1108     if (g_object_get_data( tbl, "freeze" )) {
1109         return;
1110     }
1112     // in turn, prevent listener from responding
1113     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1115     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1116     SPUnit const *unit = tracker->getActiveUnit();
1118     ShapeEditor *shape_editor = get_current_shape_editor();
1119     if (shape_editor && shape_editor->has_nodepath()) {
1120         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1121         int n_selected = 0;
1122         if (nodepath) {
1123             n_selected = nodepath->numSelected();
1124         }
1126         if (n_selected == 0) {
1127             gtk_action_set_sensitive(xact, FALSE);
1128             gtk_action_set_sensitive(yact, FALSE);
1129         } else {
1130             gtk_action_set_sensitive(xact, TRUE);
1131             gtk_action_set_sensitive(yact, TRUE);
1132             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1133             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1135             if (n_selected == 1) {
1136                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1137                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1138                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1139                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1140                 }
1141             } else {
1142                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1143                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1144                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1145                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1146                        selected nodes don't coincide (in this case we use the coordinates of the center
1147                        of the bounding box). So the entries are never set to zero. */
1148                     // FIXME: Maybe we should clear the entry if several nodes are selected
1149                     //        instead of providing a kind of average value
1150                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1151                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1152                 }
1153             }
1154         }
1155     } else {
1156         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1157         gtk_action_set_sensitive(xact, FALSE);
1158         gtk_action_set_sensitive(yact, FALSE);
1159     }
1161     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1164 static void
1165 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1167     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1168     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1170     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1171     SPUnit const *unit = tracker->getActiveUnit();
1173     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1174         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1175     }
1177     // quit if run by the attr_changed listener
1178     if (g_object_get_data( tbl, "freeze" )) {
1179         return;
1180     }
1182     // in turn, prevent listener from responding
1183     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1185     ShapeEditor *shape_editor = get_current_shape_editor();
1186     if (shape_editor && shape_editor->has_nodepath()) {
1187         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1188         if (!strcmp(value_name, "x")) {
1189             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1190         }
1191         if (!strcmp(value_name, "y")) {
1192             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1193         }
1194     }
1196     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1199 static void
1200 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1202     sp_node_path_value_changed(adj, tbl, "x");
1205 static void
1206 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1208     sp_node_path_value_changed(adj, tbl, "y");
1211 void
1212 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1214     {
1215     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1216     SPItem *item = selection->singleItem();
1217     if (item && SP_IS_LPE_ITEM(item)) {
1218        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1219            gtk_action_set_sensitive(w, TRUE);
1220        } else {
1221            gtk_action_set_sensitive(w, FALSE);
1222        }
1223     } else {
1224        gtk_action_set_sensitive(w, FALSE);
1225     }
1226     }
1228     {
1229     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1230     SPItem *item = selection->singleItem();
1231     if (item && item->clip_ref && item->clip_ref->getObject()) {
1232        gtk_action_set_sensitive(w, TRUE);
1233     } else {
1234        gtk_action_set_sensitive(w, FALSE);
1235     }
1236     }
1238     {
1239     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1240     SPItem *item = selection->singleItem();
1241     if (item && item->mask_ref && item->mask_ref->getObject()) {
1242        gtk_action_set_sensitive(w, TRUE);
1243     } else {
1244        gtk_action_set_sensitive(w, FALSE);
1245     }
1246     }
1249 void
1250 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1252     sp_node_toolbox_sel_changed (selection, tbl);
1257 //################################
1258 //##    Node Editing Toolbox    ##
1259 //################################
1261 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1263     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1264     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1265     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1266     g_object_set_data( holder, "tracker", tracker );
1268     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1270     {
1271         InkAction* inky = ink_action_new( "NodeInsertAction",
1272                                           _("Insert node"),
1273                                           _("Insert new nodes into selected segments"),
1274                                           "node_insert",
1275                                           secondarySize );
1276         g_object_set( inky, "short_label", _("Insert"), NULL );
1277         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1278         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1279     }
1281     {
1282         InkAction* inky = ink_action_new( "NodeDeleteAction",
1283                                           _("Delete node"),
1284                                           _("Delete selected nodes"),
1285                                           "node_delete",
1286                                           secondarySize );
1287         g_object_set( inky, "short_label", _("Delete"), NULL );
1288         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1289         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290     }
1292     {
1293         InkAction* inky = ink_action_new( "NodeJoinAction",
1294                                           _("Join endnodes"),
1295                                           _("Join selected endnodes"),
1296                                           "node_join",
1297                                           secondarySize );
1298         g_object_set( inky, "short_label", _("Join"), NULL );
1299         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1300         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1301     }
1303     {
1304         InkAction* inky = ink_action_new( "NodeBreakAction",
1305                                           _("Break nodes"),
1306                                           _("Break path at selected nodes"),
1307                                           "node_break",
1308                                           secondarySize );
1309         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1310         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1311     }
1314     {
1315         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1316                                           _("Join with segment"),
1317                                           _("Join selected endnodes with a new segment"),
1318                                           "node_join_segment",
1319                                           secondarySize );
1320         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1321         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1322     }
1324     {
1325         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1326                                           _("Delete segment"),
1327                                           _("Delete segment between two non-endpoint nodes"),
1328                                           "node_delete_segment",
1329                                           secondarySize );
1330         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1331         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1332     }
1334     {
1335         InkAction* inky = ink_action_new( "NodeCuspAction",
1336                                           _("Node Cusp"),
1337                                           _("Make selected nodes corner"),
1338                                           "node_cusp",
1339                                           secondarySize );
1340         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1341         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1342     }
1344     {
1345         InkAction* inky = ink_action_new( "NodeSmoothAction",
1346                                           _("Node Smooth"),
1347                                           _("Make selected nodes smooth"),
1348                                           "node_smooth",
1349                                           secondarySize );
1350         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1351         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1352     }
1354     {
1355         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1356                                           _("Node Symmetric"),
1357                                           _("Make selected nodes symmetric"),
1358                                           "node_symmetric",
1359                                           secondarySize );
1360         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1361         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1362     }
1364     {
1365         InkAction* inky = ink_action_new( "NodeAutoAction",
1366                                           _("Node Auto"),
1367                                           _("Make selected nodes auto-smooth"),
1368                                           "node_auto",
1369                                           secondarySize );
1370         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1371         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1372     }
1374     {
1375         InkAction* inky = ink_action_new( "NodeLineAction",
1376                                           _("Node Line"),
1377                                           _("Make selected segments lines"),
1378                                           "node_line",
1379                                           secondarySize );
1380         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1381         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1382     }
1384     {
1385         InkAction* inky = ink_action_new( "NodeCurveAction",
1386                                           _("Node Curve"),
1387                                           _("Make selected segments curves"),
1388                                           "node_curve",
1389                                           secondarySize );
1390         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1391         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1392     }
1394     {
1395         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1396                                                       _("Show Handles"),
1397                                                       _("Show the Bezier handles of selected nodes"),
1398                                                       "nodes_show_handles",
1399                                                       Inkscape::ICON_SIZE_DECORATION );
1400         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1401         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1402         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1403     }
1405     {
1406         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1407                                                       _("Show Outline"),
1408                                                       _("Show the outline of the path"),
1409                                                       "nodes_show_helperpath",
1410                                                       Inkscape::ICON_SIZE_DECORATION );
1411         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1412         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1413         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1414     }
1416     {
1417         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1418                                           _("Next path effect parameter"),
1419                                           _("Show next path effect parameter for editing"),
1420                                           "edit_next_parameter",
1421                                           Inkscape::ICON_SIZE_DECORATION );
1422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424         g_object_set_data( holder, "nodes_lpeedit", inky);
1425     }
1427     {
1428         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1429                                           _("Edit clipping path"),
1430                                           _("Edit the clipping path of the object"),
1431                                           "nodeedit-clippath",
1432                                           Inkscape::ICON_SIZE_DECORATION );
1433         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1434         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1435         g_object_set_data( holder, "nodes_clippathedit", inky);
1436     }
1438     {
1439         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1440                                           _("Edit mask path"),
1441                                           _("Edit the mask of the object"),
1442                                           "nodeedit-mask",
1443                                           Inkscape::ICON_SIZE_DECORATION );
1444         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1445         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1446         g_object_set_data( holder, "nodes_maskedit", inky);
1447     }
1449     /* X coord of selected node(s) */
1450     {
1451         EgeAdjustmentAction* eact = 0;
1452         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1453         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1454         eact = create_adjustment_action( "NodeXAction",
1455                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1456                                          "/tools/nodes/Xcoord", 0,
1457                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1458                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1459                                          labels, values, G_N_ELEMENTS(labels),
1460                                          sp_node_path_x_value_changed );
1461         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1462         g_object_set_data( holder, "nodes_x_action", eact );
1463         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1464         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1465     }
1467     /* Y coord of selected node(s) */
1468     {
1469         EgeAdjustmentAction* eact = 0;
1470         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1471         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1472         eact = create_adjustment_action( "NodeYAction",
1473                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1474                                          "/tools/nodes/Ycoord", 0,
1475                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1476                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1477                                          labels, values, G_N_ELEMENTS(labels),
1478                                          sp_node_path_y_value_changed );
1479         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1480         g_object_set_data( holder, "nodes_y_action", eact );
1481         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1482         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1483     }
1485     // add the units menu
1486     {
1487         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1488         gtk_action_group_add_action( mainActions, act );
1489     }
1492     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1494     //watch selection
1495     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1497     sigc::connection *c_selection_changed =
1498         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1499                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1500     pool->add_connection ("selection-changed", c_selection_changed);
1502     sigc::connection *c_selection_modified =
1503         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1504                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1505     pool->add_connection ("selection-modified", c_selection_modified);
1507     sigc::connection *c_subselection_changed =
1508         new sigc::connection (desktop->connectToolSubselectionChanged
1509                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1510     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1512     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1514     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1515 } // end of sp_node_toolbox_prep()
1518 //########################
1519 //##    Zoom Toolbox    ##
1520 //########################
1522 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1524     // no custom GtkAction setup needed
1525 } // end of sp_zoom_toolbox_prep()
1527 void
1528 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1530     toolbox_set_desktop(toolbox,
1531                         desktop,
1532                         setup_tool_toolbox,
1533                         update_tool_toolbox,
1534                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1535                                                                          "event_context_connection")));
1539 void
1540 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1542     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1543                         desktop,
1544                         setup_aux_toolbox,
1545                         update_aux_toolbox,
1546                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1547                                                                          "event_context_connection")));
1550 void
1551 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1553     toolbox_set_desktop(toolbox,
1554                         desktop,
1555                         setup_commands_toolbox,
1556                         update_commands_toolbox,
1557                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1558                                                                          "event_context_connection")));
1561 static void
1562 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1564     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1565     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1567     if (old_desktop) {
1568         GList *children, *iter;
1570         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1571         for ( iter = children ; iter ; iter = iter->next ) {
1572             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1573         }
1574         g_list_free(children);
1575     }
1577     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1579     if (desktop) {
1580         gtk_widget_set_sensitive(toolbox, TRUE);
1581         setup_func(toolbox, desktop);
1582         update_func(desktop, desktop->event_context, toolbox);
1583         *conn = desktop->connectEventContextChanged
1584             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1585     } else {
1586         gtk_widget_set_sensitive(toolbox, FALSE);
1587     }
1589 } // end of toolbox_set_desktop()
1592 static void
1593 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1595     gchar const * descr =
1596         "<ui>"
1597         "  <toolbar name='ToolToolbar'>"
1598         "    <toolitem action='ToolSelector' />"
1599         "    <toolitem action='ToolNode' />"
1600         "    <toolitem action='ToolTweak' />"
1601         "    <toolitem action='ToolZoom' />"
1602         "    <toolitem action='ToolRect' />"
1603         "    <toolitem action='Tool3DBox' />"
1604         "    <toolitem action='ToolArc' />"
1605         "    <toolitem action='ToolStar' />"
1606         "    <toolitem action='ToolSpiral' />"
1607         "    <toolitem action='ToolPencil' />"
1608         "    <toolitem action='ToolPen' />"
1609         "    <toolitem action='ToolCalligraphic' />"
1610         "    <toolitem action='ToolEraser' />"
1611 //        "    <toolitem action='ToolLPETool' />"
1612         "    <toolitem action='ToolPaintBucket' />"
1613         "    <toolitem action='ToolText' />"
1614         "    <toolitem action='ToolConnector' />"
1615         "    <toolitem action='ToolGradient' />"
1616         "    <toolitem action='ToolDropper' />"
1617         "  </toolbar>"
1618         "</ui>";
1619     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1620     GtkUIManager* mgr = gtk_ui_manager_new();
1621     GError* errVal = 0;
1622     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1624     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1625     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1627     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1628     if ( prefs->getBool("/toolbox/icononly", true) ) {
1629         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1630     }
1631     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1632     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1634     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1635     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1637     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1639     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1640     if ( child ) {
1641         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1642     }
1644     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1645 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1649 static void
1650 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1652     gchar const *const tname = ( eventcontext
1653                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1654                                  : NULL );
1655     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1657     for (int i = 0 ; tools[i].type_name ; i++ ) {
1658         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1659         if ( act ) {
1660             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1661             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1662             if ( verbAct ) {
1663                 verbAct->set_active(setActive);
1664             }
1665         }
1666     }
1669 static void
1670 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1672     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1673     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1674     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1675     GtkUIManager* mgr = gtk_ui_manager_new();
1676     GError* errVal = 0;
1677     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1678     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1680     std::map<std::string, GtkWidget*> dataHolders;
1682     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1683         if ( aux_toolboxes[i].prep_func ) {
1684             // converted to GtkActions and UIManager
1686             GtkWidget* kludge = gtk_toolbar_new();
1687             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1688             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1689             dataHolders[aux_toolboxes[i].type_name] = kludge;
1690             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1691         } else {
1693             GtkWidget *sub_toolbox = 0;
1694             if (aux_toolboxes[i].create_func == NULL)
1695                 sub_toolbox = sp_empty_toolbox_new(desktop);
1696             else {
1697                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1698             }
1700             gtk_size_group_add_widget( grouper, sub_toolbox );
1702             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1703             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1705         }
1706     }
1708     // Second pass to create toolbars *after* all GtkActions are created
1709     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1710         if ( aux_toolboxes[i].prep_func ) {
1711             // converted to GtkActions and UIManager
1713             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1715             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1716             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1718             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1719             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1720             g_free( tmp );
1721             tmp = 0;
1723             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1724             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1725                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1726             }
1727             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1730             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1732             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1733                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1734                 swatch->setDesktop( desktop );
1735                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1736                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1737                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1738                 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 );
1739             }
1741             gtk_widget_show_all( holder );
1742             sp_set_font_size_smaller( holder );
1744             gtk_size_group_add_widget( grouper, holder );
1746             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1747             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1748         }
1749     }
1751     g_object_unref( G_OBJECT(grouper) );
1754 static void
1755 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1757     gchar const *tname = ( eventcontext
1758                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1759                            : NULL );
1760     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1761         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1762         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1763             gtk_widget_show_all(sub_toolbox);
1764             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1765         } else {
1766             gtk_widget_hide(sub_toolbox);
1767         }
1768     }
1771 static void
1772 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1774     gchar const * descr =
1775         "<ui>"
1776         "  <toolbar name='CommandsToolbar'>"
1777         "    <toolitem action='FileNew' />"
1778         "    <toolitem action='FileOpen' />"
1779         "    <toolitem action='FileSave' />"
1780         "    <toolitem action='FilePrint' />"
1781         "    <separator />"
1782         "    <toolitem action='FileImport' />"
1783         "    <toolitem action='FileExport' />"
1784         "    <separator />"
1785         "    <toolitem action='EditUndo' />"
1786         "    <toolitem action='EditRedo' />"
1787         "    <separator />"
1788         "    <toolitem action='EditCopy' />"
1789         "    <toolitem action='EditCut' />"
1790         "    <toolitem action='EditPaste' />"
1791         "    <separator />"
1792         "    <toolitem action='ZoomSelection' />"
1793         "    <toolitem action='ZoomDrawing' />"
1794         "    <toolitem action='ZoomPage' />"
1795         "    <separator />"
1796         "    <toolitem action='EditDuplicate' />"
1797         "    <toolitem action='EditClone' />"
1798         "    <toolitem action='EditUnlinkClone' />"
1799         "    <separator />"
1800         "    <toolitem action='SelectionGroup' />"
1801         "    <toolitem action='SelectionUnGroup' />"
1802         "    <separator />"
1803         "    <toolitem action='DialogFillStroke' />"
1804         "    <toolitem action='DialogText' />"
1805         "    <toolitem action='DialogXMLEditor' />"
1806         "    <toolitem action='DialogAlignDistribute' />"
1807         "    <separator />"
1808         "    <toolitem action='DialogPreferences' />"
1809         "    <toolitem action='DialogDocumentProperties' />"
1810         "  </toolbar>"
1811         "</ui>";
1812     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1813     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1815     GtkUIManager* mgr = gtk_ui_manager_new();
1816     GError* errVal = 0;
1818     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1819     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1821     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1822     if ( prefs->getBool("/toolbox/icononly", true) ) {
1823         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1824     }
1826     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1827     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1829     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1830     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1833     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1835     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1836     if ( child ) {
1837         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1838     }
1840     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1843 static void
1844 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1848 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1850     gtk_widget_show(toolbox_toplevel);
1851     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1853     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1854     if (!shown_toolbox) {
1855         return;
1856     }
1857     gtk_widget_show(toolbox);
1859     gtk_widget_show_all(shown_toolbox);
1862 static GtkWidget *
1863 sp_empty_toolbox_new(SPDesktop *desktop)
1865     GtkWidget *tbl = gtk_toolbar_new();
1866     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1867     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1869     gtk_widget_show_all(tbl);
1870     sp_set_font_size_smaller (tbl);
1872     return tbl;
1875 #define MODE_LABEL_WIDTH 70
1877 //########################
1878 //##       Star         ##
1879 //########################
1881 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1883     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1885     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1886         // do not remember prefs if this call is initiated by an undo change, because undoing object
1887         // creation sets bogus values to its attributes before it is deleted
1888         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1889         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
1890     }
1892     // quit if run by the attr_changed listener
1893     if (g_object_get_data( dataKludge, "freeze" )) {
1894         return;
1895     }
1897     // in turn, prevent listener from responding
1898     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1900     bool modmade = false;
1902     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1903     GSList const *items = selection->itemList();
1904     for (; items != NULL; items = items->next) {
1905         if (SP_IS_STAR((SPItem *) items->data)) {
1906             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1907             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1908             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1909                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1910                                     + M_PI / (gint)adj->value));
1911             SP_OBJECT((SPItem *) items->data)->updateRepr();
1912             modmade = true;
1913         }
1914     }
1915     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1916                                    _("Star: Change number of corners"));
1918     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1921 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1923     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1925     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1926         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1927         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
1928     }
1930     // quit if run by the attr_changed listener
1931     if (g_object_get_data( dataKludge, "freeze" )) {
1932         return;
1933     }
1935     // in turn, prevent listener from responding
1936     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1938     bool modmade = false;
1939     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1940     GSList const *items = selection->itemList();
1941     for (; items != NULL; items = items->next) {
1942         if (SP_IS_STAR((SPItem *) items->data)) {
1943             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1945             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1946             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1947             if (r2 < r1) {
1948                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1949             } else {
1950                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1951             }
1953             SP_OBJECT((SPItem *) items->data)->updateRepr();
1954             modmade = true;
1955         }
1956     }
1958     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1959                                    _("Star: Change spoke ratio"));
1961     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1964 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1966     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1967     bool flat = ege_select_one_action_get_active( act ) == 0;
1969     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1970         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1971         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
1972     }
1974     // quit if run by the attr_changed listener
1975     if (g_object_get_data( dataKludge, "freeze" )) {
1976         return;
1977     }
1979     // in turn, prevent listener from responding
1980     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1982     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1983     GSList const *items = selection->itemList();
1984     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1985     bool modmade = false;
1987     if ( prop_action ) {
1988         gtk_action_set_sensitive( prop_action, !flat );
1989     }
1991     for (; items != NULL; items = items->next) {
1992         if (SP_IS_STAR((SPItem *) items->data)) {
1993             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1994             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1995             SP_OBJECT((SPItem *) items->data)->updateRepr();
1996             modmade = true;
1997         }
1998     }
2000     if (modmade) {
2001         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2002                          flat ? _("Make polygon") : _("Make star"));
2003     }
2005     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2008 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2010     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2012     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2013         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2014         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2015     }
2017     // quit if run by the attr_changed listener
2018     if (g_object_get_data( dataKludge, "freeze" )) {
2019         return;
2020     }
2022     // in turn, prevent listener from responding
2023     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2025     bool modmade = false;
2027     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2028     GSList const *items = selection->itemList();
2029     for (; items != NULL; items = items->next) {
2030         if (SP_IS_STAR((SPItem *) items->data)) {
2031             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2032             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2033             SP_OBJECT(items->data)->updateRepr();
2034             modmade = true;
2035         }
2036     }
2037     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2038                                    _("Star: Change rounding"));
2040     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2043 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2045     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2047     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2048         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2049         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2050     }
2052     // quit if run by the attr_changed listener
2053     if (g_object_get_data( dataKludge, "freeze" )) {
2054         return;
2055     }
2057     // in turn, prevent listener from responding
2058     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2060     bool modmade = false;
2062     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2063     GSList const *items = selection->itemList();
2064     for (; items != NULL; items = items->next) {
2065         if (SP_IS_STAR((SPItem *) items->data)) {
2066             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2067             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2068             SP_OBJECT(items->data)->updateRepr();
2069             modmade = true;
2070         }
2071     }
2072     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2073                                    _("Star: Change randomization"));
2075     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2079 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2080                                        gchar const */*old_value*/, gchar const */*new_value*/,
2081                                        bool /*is_interactive*/, gpointer data)
2083     GtkWidget *tbl = GTK_WIDGET(data);
2085     // quit if run by the _changed callbacks
2086     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2087         return;
2088     }
2090     // in turn, prevent callbacks from responding
2091     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2093     GtkAdjustment *adj = 0;
2095     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2096     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2098     if (!strcmp(name, "inkscape:randomized")) {
2099         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2100         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2101     } else if (!strcmp(name, "inkscape:rounded")) {
2102         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2103         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2104     } else if (!strcmp(name, "inkscape:flatsided")) {
2105         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2106         char const *flatsides = repr->attribute("inkscape:flatsided");
2107         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2108         if ( flatsides && !strcmp(flatsides,"false") ) {
2109             ege_select_one_action_set_active( flat_action, 1 );
2110             gtk_action_set_sensitive( prop_action, TRUE );
2111         } else {
2112             ege_select_one_action_set_active( flat_action, 0 );
2113             gtk_action_set_sensitive( prop_action, FALSE );
2114         }
2115     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2116         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2117         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2118         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2119         if (r2 < r1) {
2120             gtk_adjustment_set_value(adj, r2/r1);
2121         } else {
2122             gtk_adjustment_set_value(adj, r1/r2);
2123         }
2124     } else if (!strcmp(name, "sodipodi:sides")) {
2125         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2126         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2127     }
2129     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2133 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2135     NULL, /* child_added */
2136     NULL, /* child_removed */
2137     star_tb_event_attr_changed,
2138     NULL, /* content_changed */
2139     NULL  /* order_changed */
2140 };
2143 /**
2144  *  \param selection Should not be NULL.
2145  */
2146 static void
2147 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2149     int n_selected = 0;
2150     Inkscape::XML::Node *repr = NULL;
2152     purge_repr_listener( tbl, tbl );
2154     for (GSList const *items = selection->itemList();
2155          items != NULL;
2156          items = items->next)
2157     {
2158         if (SP_IS_STAR((SPItem *) items->data)) {
2159             n_selected++;
2160             repr = SP_OBJECT_REPR((SPItem *) items->data);
2161         }
2162     }
2164     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2166     if (n_selected == 0) {
2167         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2168     } else if (n_selected == 1) {
2169         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2171         if (repr) {
2172             g_object_set_data( tbl, "repr", repr );
2173             Inkscape::GC::anchor(repr);
2174             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2175             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2176         }
2177     } else {
2178         // FIXME: implement averaging of all parameters for multiple selected stars
2179         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2180         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2181     }
2185 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2187     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2188     // callbacks to lump all the changes for all selected objects in one undo step
2190     GtkAdjustment *adj = 0;
2192     // fixme: make settable in prefs!
2193     gint mag = 5;
2194     gdouble prop = 0.5;
2195     gboolean flat = FALSE;
2196     gdouble randomized = 0;
2197     gdouble rounded = 0;
2199     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2200     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2202     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2203     gtk_action_set_sensitive( sb2, !flat );
2205     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2206     gtk_adjustment_set_value(adj, mag);
2207     gtk_adjustment_value_changed(adj);
2209     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2210     gtk_adjustment_set_value(adj, prop);
2211     gtk_adjustment_value_changed(adj);
2213     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2214     gtk_adjustment_set_value(adj, rounded);
2215     gtk_adjustment_value_changed(adj);
2217     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2218     gtk_adjustment_set_value(adj, randomized);
2219     gtk_adjustment_value_changed(adj);
2223 void
2224 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2226     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2227     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2228     GtkWidget *l = gtk_label_new(NULL);
2229     gtk_label_set_markup(GTK_LABEL(l), title);
2230     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2231     if ( GTK_IS_TOOLBAR(tbl) ) {
2232         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2233     } else {
2234         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2235     }
2236     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2240 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2242     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2244     {
2245         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2246         ege_output_action_set_use_markup( act, TRUE );
2247         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2248         g_object_set_data( holder, "mode_action", act );
2249     }
2251     {
2252         EgeAdjustmentAction* eact = 0;
2253         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2254         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2256         /* Flatsided checkbox */
2257         {
2258             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2260             GtkTreeIter iter;
2261             gtk_list_store_append( model, &iter );
2262             gtk_list_store_set( model, &iter,
2263                                 0, _("Polygon"),
2264                                 1, _("Regular polygon (with one handle) instead of a star"),
2265                                 2, "star_flat",
2266                                 -1 );
2268             gtk_list_store_append( model, &iter );
2269             gtk_list_store_set( model, &iter,
2270                                 0, _("Star"),
2271                                 1, _("Star instead of a regular polygon (with one handle)"),
2272                                 2, "star_angled",
2273                                 -1 );
2275             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2276             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2277             g_object_set_data( holder, "flat_action", act );
2279             ege_select_one_action_set_appearance( act, "full" );
2280             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2281             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2282             ege_select_one_action_set_icon_column( act, 2 );
2283             ege_select_one_action_set_icon_size( act, secondarySize );
2284             ege_select_one_action_set_tooltip_column( act, 1  );
2286             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2287             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2288         }
2290         /* Magnitude */
2291         {
2292         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2293         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2294         eact = create_adjustment_action( "MagnitudeAction",
2295                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2296                                          "/tools/shapes/star/magnitude", 3,
2297                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2298                                          3, 1024, 1, 5,
2299                                          labels, values, G_N_ELEMENTS(labels),
2300                                          sp_stb_magnitude_value_changed,
2301                                          1.0, 0 );
2302         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2303         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2304         }
2306         /* Spoke ratio */
2307         {
2308         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2309         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2310         eact = create_adjustment_action( "SpokeAction",
2311                                          _("Spoke ratio"), _("Spoke ratio:"),
2312                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2313                                          // Base radius is the same for the closest handle.
2314                                          _("Base radius to tip radius ratio"),
2315                                          "/tools/shapes/star/proportion", 0.5,
2316                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2317                                          0.01, 1.0, 0.01, 0.1,
2318                                          labels, values, G_N_ELEMENTS(labels),
2319                                          sp_stb_proportion_value_changed );
2320         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2321         g_object_set_data( holder, "prop_action", eact );
2322         }
2324         if ( !isFlatSided ) {
2325             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2326         } else {
2327             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2328         }
2330         /* Roundedness */
2331         {
2332         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2333         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2334         eact = create_adjustment_action( "RoundednessAction",
2335                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2336                                          "/tools/shapes/star/rounded", 0.0,
2337                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2338                                          -10.0, 10.0, 0.01, 0.1,
2339                                          labels, values, G_N_ELEMENTS(labels),
2340                                          sp_stb_rounded_value_changed );
2341         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2342         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2343         }
2345         /* Randomization */
2346         {
2347         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2348         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2349         eact = create_adjustment_action( "RandomizationAction",
2350                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2351                                          "/tools/shapes/star/randomized", 0.0,
2352                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2353                                          -10.0, 10.0, 0.001, 0.01,
2354                                          labels, values, G_N_ELEMENTS(labels),
2355                                          sp_stb_randomized_value_changed, 0.1, 3 );
2356         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2357         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2358         }
2359     }
2361     {
2362         /* Reset */
2363         {
2364             GtkAction* act = gtk_action_new( "StarResetAction",
2365                                              _("Defaults"),
2366                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2367                                              GTK_STOCK_CLEAR );
2368             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2369             gtk_action_group_add_action( mainActions, act );
2370             gtk_action_set_sensitive( act, TRUE );
2371         }
2372     }
2374     sigc::connection *connection = new sigc::connection(
2375         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2376         );
2377     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2378     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2382 //########################
2383 //##       Rect         ##
2384 //########################
2386 static void sp_rtb_sensitivize( GObject *tbl )
2388     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2389     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2390     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2392     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2393         gtk_action_set_sensitive( not_rounded, FALSE );
2394     } else {
2395         gtk_action_set_sensitive( not_rounded, TRUE );
2396     }
2400 static void
2401 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2402                           void (*setter)(SPRect *, gdouble))
2404     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2406     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2407     SPUnit const *unit = tracker->getActiveUnit();
2409     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2410         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2411         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2412     }
2414     // quit if run by the attr_changed listener
2415     if (g_object_get_data( tbl, "freeze" )) {
2416         return;
2417     }
2419     // in turn, prevent listener from responding
2420     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2422     bool modmade = false;
2423     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2424     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2425         if (SP_IS_RECT(items->data)) {
2426             if (adj->value != 0) {
2427                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2428             } else {
2429                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2430             }
2431             modmade = true;
2432         }
2433     }
2435     sp_rtb_sensitivize( tbl );
2437     if (modmade) {
2438         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2439                                    _("Change rectangle"));
2440     }
2442     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2445 static void
2446 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2448     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2451 static void
2452 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2454     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2457 static void
2458 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2460     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2463 static void
2464 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2466     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2471 static void
2472 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2474     GtkAdjustment *adj = 0;
2476     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2477     gtk_adjustment_set_value(adj, 0.0);
2478     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2479     gtk_adjustment_value_changed(adj);
2481     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2482     gtk_adjustment_set_value(adj, 0.0);
2483     gtk_adjustment_value_changed(adj);
2485     sp_rtb_sensitivize( obj );
2488 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2489                                        gchar const */*old_value*/, gchar const */*new_value*/,
2490                                        bool /*is_interactive*/, gpointer data)
2492     GObject *tbl = G_OBJECT(data);
2494     // quit if run by the _changed callbacks
2495     if (g_object_get_data( tbl, "freeze" )) {
2496         return;
2497     }
2499     // in turn, prevent callbacks from responding
2500     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2502     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2503     SPUnit const *unit = tracker->getActiveUnit();
2505     gpointer item = g_object_get_data( tbl, "item" );
2506     if (item && SP_IS_RECT(item)) {
2507         {
2508             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2509             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2510             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2511         }
2513         {
2514             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2515             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2516             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2517         }
2519         {
2520             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2521             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2522             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2523         }
2525         {
2526             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2527             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2528             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2529         }
2530     }
2532     sp_rtb_sensitivize( tbl );
2534     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2538 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2539     NULL, /* child_added */
2540     NULL, /* child_removed */
2541     rect_tb_event_attr_changed,
2542     NULL, /* content_changed */
2543     NULL  /* order_changed */
2544 };
2546 /**
2547  *  \param selection should not be NULL.
2548  */
2549 static void
2550 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2552     int n_selected = 0;
2553     Inkscape::XML::Node *repr = NULL;
2554     SPItem *item = NULL;
2556     if ( g_object_get_data( tbl, "repr" ) ) {
2557         g_object_set_data( tbl, "item", NULL );
2558     }
2559     purge_repr_listener( tbl, tbl );
2561     for (GSList const *items = selection->itemList();
2562          items != NULL;
2563          items = items->next) {
2564         if (SP_IS_RECT((SPItem *) items->data)) {
2565             n_selected++;
2566             item = (SPItem *) items->data;
2567             repr = SP_OBJECT_REPR(item);
2568         }
2569     }
2571     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2573     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2575     if (n_selected == 0) {
2576         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2578         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2579         gtk_action_set_sensitive(w, FALSE);
2580         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2581         gtk_action_set_sensitive(h, FALSE);
2583     } else if (n_selected == 1) {
2584         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2585         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2587         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2588         gtk_action_set_sensitive(w, TRUE);
2589         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2590         gtk_action_set_sensitive(h, TRUE);
2592         if (repr) {
2593             g_object_set_data( tbl, "repr", repr );
2594             g_object_set_data( tbl, "item", item );
2595             Inkscape::GC::anchor(repr);
2596             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2597             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2598         }
2599     } else {
2600         // FIXME: implement averaging of all parameters for multiple selected
2601         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2602         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2603         sp_rtb_sensitivize( tbl );
2604     }
2608 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2610     EgeAdjustmentAction* eact = 0;
2611     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2613     {
2614         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2615         ege_output_action_set_use_markup( act, TRUE );
2616         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2617         g_object_set_data( holder, "mode_action", act );
2618     }
2620     // rx/ry units menu: create
2621     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2622     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2623     // fixme: add % meaning per cent of the width/height
2624     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2625     g_object_set_data( holder, "tracker", tracker );
2627     /* W */
2628     {
2629         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2630         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2631         eact = create_adjustment_action( "RectWidthAction",
2632                                          _("Width"), _("W:"), _("Width of rectangle"),
2633                                          "/tools/shapes/rect/width", 0,
2634                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2635                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2636                                          labels, values, G_N_ELEMENTS(labels),
2637                                          sp_rtb_width_value_changed );
2638         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2639         g_object_set_data( holder, "width_action", eact );
2640         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2641         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2642     }
2644     /* H */
2645     {
2646         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2647         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2648         eact = create_adjustment_action( "RectHeightAction",
2649                                          _("Height"), _("H:"), _("Height of rectangle"),
2650                                          "/tools/shapes/rect/height", 0,
2651                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2652                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2653                                          labels, values, G_N_ELEMENTS(labels),
2654                                          sp_rtb_height_value_changed );
2655         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2656         g_object_set_data( holder, "height_action", eact );
2657         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2658         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2659     }
2661     /* rx */
2662     {
2663         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2664         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2665         eact = create_adjustment_action( "RadiusXAction",
2666                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2667                                          "/tools/shapes/rect/rx", 0,
2668                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2669                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2670                                          labels, values, G_N_ELEMENTS(labels),
2671                                          sp_rtb_rx_value_changed);
2672         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2673         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2674     }
2676     /* ry */
2677     {
2678         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2679         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2680         eact = create_adjustment_action( "RadiusYAction",
2681                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2682                                          "/tools/shapes/rect/ry", 0,
2683                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2684                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2685                                          labels, values, G_N_ELEMENTS(labels),
2686                                          sp_rtb_ry_value_changed);
2687         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2688         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2689     }
2691     // add the units menu
2692     {
2693         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2694         gtk_action_group_add_action( mainActions, act );
2695     }
2697     /* Reset */
2698     {
2699         InkAction* inky = ink_action_new( "RectResetAction",
2700                                           _("Not rounded"),
2701                                           _("Make corners sharp"),
2702                                           "squared_corner",
2703                                           secondarySize );
2704         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2705         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2706         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2707         g_object_set_data( holder, "not_rounded", inky );
2708     }
2710     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2711     sp_rtb_sensitivize( holder );
2713     sigc::connection *connection = new sigc::connection(
2714         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2715         );
2716     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2717     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2720 //########################
2721 //##       3D Box       ##
2722 //########################
2724 // normalize angle so that it lies in the interval [0,360]
2725 static double box3d_normalize_angle (double a) {
2726     double angle = a + ((int) (a/360.0))*360;
2727     if (angle < 0) {
2728         angle += 360.0;
2729     }
2730     return angle;
2733 static void
2734 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2735                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2736     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2737     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2738     //       are reset).
2739     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2741     if (is_infinite) {
2742         gtk_toggle_action_set_active(tact, TRUE);
2743         gtk_action_set_sensitive(act, TRUE);
2745         double angle = persp3d_get_infinite_angle(persp, axis);
2746         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2747             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2748         }
2749     } else {
2750         gtk_toggle_action_set_active(tact, FALSE);
2751         gtk_action_set_sensitive(act, FALSE);
2752     }
2755 static void
2756 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2757     if (!persp_repr) {
2758         g_print ("No perspective given to box3d_resync_toolbar().\n");
2759         return;
2760     }
2762     GtkWidget *tbl = GTK_WIDGET(data);
2763     GtkAdjustment *adj = 0;
2764     GtkAction *act = 0;
2765     GtkToggleAction *tact = 0;
2766     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2767     {
2768         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2769         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2770         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2772         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2773     }
2774     {
2775         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2776         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2777         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2779         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2780     }
2781     {
2782         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2783         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2784         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2786         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2787     }
2790 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2791                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2792                                                   bool /*is_interactive*/, gpointer data)
2794     GtkWidget *tbl = GTK_WIDGET(data);
2796     // quit if run by the attr_changed or selection changed listener
2797     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2798         return;
2799     }
2801     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2802     // sp_document_maybe_done() when the document is undo insensitive)
2803     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2805     // TODO: Only update the appropriate part of the toolbar
2806 //    if (!strcmp(name, "inkscape:vp_z")) {
2807         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2808 //    }
2810     Persp3D *persp = persp3d_get_from_repr(repr);
2811     persp3d_update_box_reprs(persp);
2813     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2816 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2818     NULL, /* child_added */
2819     NULL, /* child_removed */
2820     box3d_persp_tb_event_attr_changed,
2821     NULL, /* content_changed */
2822     NULL  /* order_changed */
2823 };
2825 /**
2826  *  \param selection Should not be NULL.
2827  */
2828 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2829 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2830 static void
2831 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2833     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2834     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2835     // update the perspectives with infinite VPs and leave the other ones untouched).
2837     Inkscape::XML::Node *persp_repr = NULL;
2838     purge_repr_listener(tbl, tbl);
2840     SPItem *item = selection->singleItem();
2841     if (item && SP_IS_BOX3D(item)) {
2842         // FIXME: Also deal with multiple selected boxes
2843         SPBox3D *box = SP_BOX3D(item);
2844         Persp3D *persp = box3d_get_perspective(box);
2845         persp_repr = SP_OBJECT_REPR(persp);
2846         if (persp_repr) {
2847             g_object_set_data(tbl, "repr", persp_repr);
2848             Inkscape::GC::anchor(persp_repr);
2849             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2850             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2851         }
2853         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2854         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2855         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
2857         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
2858         box3d_resync_toolbar(persp_repr, tbl);
2859         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
2860     }
2863 static void
2864 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2866     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2867     SPDocument *document = sp_desktop_document(desktop);
2869     // quit if run by the attr_changed or selection changed listener
2870     if (g_object_get_data( dataKludge, "freeze" )) {
2871         return;
2872     }
2874     // in turn, prevent listener from responding
2875     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2877     //Persp3D *persp = document->current_persp3d;
2878     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2879     if (sel_persps.empty()) {
2880         // this can happen when the document is created; we silently ignore it
2881         return;
2882     }
2883     Persp3D *persp = sel_persps.front();
2885     persp->tmat.set_infinite_direction (axis, adj->value);
2886     SP_OBJECT(persp)->updateRepr();
2888     // TODO: use the correct axis here, too
2889     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2891     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2895 static void
2896 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2898     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2901 static void
2902 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2904     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2907 static void
2908 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2910     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2914 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2916     // TODO: Take all selected perspectives into account
2917     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2918     if (sel_persps.empty()) {
2919         // this can happen when the document is created; we silently ignore it
2920         return;
2921     }
2922     Persp3D *persp = sel_persps.front();
2924     bool set_infinite = gtk_toggle_action_get_active(act);
2925     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2928 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2930     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2933 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2935     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2938 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2940     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2943 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2945     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2946     EgeAdjustmentAction* eact = 0;
2947     SPDocument *document = sp_desktop_document (desktop);
2948     Persp3D *persp = document->current_persp3d;
2950     EgeAdjustmentAction* box3d_angle_x = 0;
2951     EgeAdjustmentAction* box3d_angle_y = 0;
2952     EgeAdjustmentAction* box3d_angle_z = 0;
2954     /* Angle X */
2955     {
2956         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2957         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2958         eact = create_adjustment_action( "3DBoxAngleXAction",
2959                                          _("Angle in X direction"), _("Angle X:"),
2960                                          // Translators: PL is short for 'perspective line'
2961                                          _("Angle of PLs in X direction"),
2962                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
2963                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2964                                          -360.0, 360.0, 1.0, 10.0,
2965                                          labels, values, G_N_ELEMENTS(labels),
2966                                          box3d_angle_x_value_changed );
2967         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2968         g_object_set_data( holder, "box3d_angle_x_action", eact );
2969         box3d_angle_x = eact;
2970     }
2972     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2973         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2974     } else {
2975         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2976     }
2979     /* VP X state */
2980     {
2981         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2982                                                       // Translators: VP is short for 'vanishing point'
2983                                                       _("State of VP in X direction"),
2984                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2985                                                       "toggle_vp_x",
2986                                                       Inkscape::ICON_SIZE_DECORATION );
2987         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2988         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2989         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2990         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
2991         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
2992     }
2994     /* Angle Y */
2995     {
2996         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2997         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2998         eact = create_adjustment_action( "3DBoxAngleYAction",
2999                                          _("Angle in Y direction"), _("Angle Y:"),
3000                                          // Translators: PL is short for 'perspective line'
3001                                          _("Angle of PLs in Y direction"),
3002                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3003                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3004                                          -360.0, 360.0, 1.0, 10.0,
3005                                          labels, values, G_N_ELEMENTS(labels),
3006                                          box3d_angle_y_value_changed );
3007         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3008         g_object_set_data( holder, "box3d_angle_y_action", eact );
3009         box3d_angle_y = eact;
3010     }
3012     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3013         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3014     } else {
3015         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3016     }
3018     /* VP Y state */
3019     {
3020         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3021                                                       // Translators: VP is short for 'vanishing point'
3022                                                       _("State of VP in Y direction"),
3023                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3024                                                       "toggle_vp_y",
3025                                                       Inkscape::ICON_SIZE_DECORATION );
3026         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3027         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3028         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3029         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3030         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3031     }
3033     /* Angle Z */
3034     {
3035         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3036         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3037         eact = create_adjustment_action( "3DBoxAngleZAction",
3038                                          _("Angle in Z direction"), _("Angle Z:"),
3039                                          // Translators: PL is short for 'perspective line'
3040                                          _("Angle of PLs in Z direction"),
3041                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3042                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3043                                          -360.0, 360.0, 1.0, 10.0,
3044                                          labels, values, G_N_ELEMENTS(labels),
3045                                          box3d_angle_z_value_changed );
3046         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3047         g_object_set_data( holder, "box3d_angle_z_action", eact );
3048         box3d_angle_z = eact;
3049     }
3051     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3052         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3053     } else {
3054         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3055     }
3057     /* VP Z state */
3058     {
3059         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3060                                                       // Translators: VP is short for 'vanishing point'
3061                                                       _("State of VP in Z direction"),
3062                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3063                                                       "toggle_vp_z",
3064                                                       Inkscape::ICON_SIZE_DECORATION );
3065         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3066         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3067         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3068         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3069         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3070     }
3072     sigc::connection *connection = new sigc::connection(
3073         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3074        );
3075     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3076     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3079 //########################
3080 //##       Spiral       ##
3081 //########################
3083 static void
3084 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3086     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3088     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3089         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3090         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3091     }
3093     // quit if run by the attr_changed listener
3094     if (g_object_get_data( tbl, "freeze" )) {
3095         return;
3096     }
3098     // in turn, prevent listener from responding
3099     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3101     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3103     bool modmade = false;
3104     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3105          items != NULL;
3106          items = items->next)
3107     {
3108         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3109             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3110             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3111             SP_OBJECT((SPItem *) items->data)->updateRepr();
3112             modmade = true;
3113         }
3114     }
3116     g_free(namespaced_name);
3118     if (modmade) {
3119         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3120                                    _("Change spiral"));
3121     }
3123     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3126 static void
3127 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3129     sp_spl_tb_value_changed(adj, tbl, "revolution");
3132 static void
3133 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3135     sp_spl_tb_value_changed(adj, tbl, "expansion");
3138 static void
3139 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3141     sp_spl_tb_value_changed(adj, tbl, "t0");
3144 static void
3145 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3147     GtkWidget *tbl = GTK_WIDGET(obj);
3149     GtkAdjustment *adj;
3151     // fixme: make settable
3152     gdouble rev = 5;
3153     gdouble exp = 1.0;
3154     gdouble t0 = 0.0;
3156     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3157     gtk_adjustment_set_value(adj, rev);
3158     gtk_adjustment_value_changed(adj);
3160     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3161     gtk_adjustment_set_value(adj, exp);
3162     gtk_adjustment_value_changed(adj);
3164     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3165     gtk_adjustment_set_value(adj, t0);
3166     gtk_adjustment_value_changed(adj);
3168     spinbutton_defocus(GTK_OBJECT(tbl));
3172 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3173                                          gchar const */*old_value*/, gchar const */*new_value*/,
3174                                          bool /*is_interactive*/, gpointer data)
3176     GtkWidget *tbl = GTK_WIDGET(data);
3178     // quit if run by the _changed callbacks
3179     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3180         return;
3181     }
3183     // in turn, prevent callbacks from responding
3184     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3186     GtkAdjustment *adj;
3187     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3188     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3190     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3191     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3193     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3194     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3196     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3200 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3201     NULL, /* child_added */
3202     NULL, /* child_removed */
3203     spiral_tb_event_attr_changed,
3204     NULL, /* content_changed */
3205     NULL  /* order_changed */
3206 };
3208 static void
3209 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3211     int n_selected = 0;
3212     Inkscape::XML::Node *repr = NULL;
3214     purge_repr_listener( tbl, tbl );
3216     for (GSList const *items = selection->itemList();
3217          items != NULL;
3218          items = items->next)
3219     {
3220         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3221             n_selected++;
3222             repr = SP_OBJECT_REPR((SPItem *) items->data);
3223         }
3224     }
3226     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3228     if (n_selected == 0) {
3229         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3230     } else if (n_selected == 1) {
3231         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3233         if (repr) {
3234             g_object_set_data( tbl, "repr", repr );
3235             Inkscape::GC::anchor(repr);
3236             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3237             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3238         }
3239     } else {
3240         // FIXME: implement averaging of all parameters for multiple selected
3241         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3242         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3243     }
3247 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3249     EgeAdjustmentAction* eact = 0;
3250     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3252     {
3253         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3254         ege_output_action_set_use_markup( act, TRUE );
3255         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3256         g_object_set_data( holder, "mode_action", act );
3257     }
3259     /* Revolution */
3260     {
3261         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3262         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3263         eact = create_adjustment_action( "SpiralRevolutionAction",
3264                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3265                                          "/tools/shapes/spiral/revolution", 3.0,
3266                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3267                                          0.01, 1024.0, 0.1, 1.0,
3268                                          labels, values, G_N_ELEMENTS(labels),
3269                                          sp_spl_tb_revolution_value_changed, 1, 2);
3270         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3271     }
3273     /* Expansion */
3274     {
3275         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3276         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3277         eact = create_adjustment_action( "SpiralExpansionAction",
3278                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3279                                          "/tools/shapes/spiral/expansion", 1.0,
3280                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3281                                          0.0, 1000.0, 0.01, 1.0,
3282                                          labels, values, G_N_ELEMENTS(labels),
3283                                          sp_spl_tb_expansion_value_changed);
3284         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3285     }
3287     /* T0 */
3288     {
3289         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3290         gdouble values[] = {0, 0.5, 0.9};
3291         eact = create_adjustment_action( "SpiralT0Action",
3292                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3293                                          "/tools/shapes/spiral/t0", 0.0,
3294                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3295                                          0.0, 0.999, 0.01, 1.0,
3296                                          labels, values, G_N_ELEMENTS(labels),
3297                                          sp_spl_tb_t0_value_changed);
3298         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3299     }
3301     /* Reset */
3302     {
3303         InkAction* inky = ink_action_new( "SpiralResetAction",
3304                                           _("Defaults"),
3305                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3306                                           GTK_STOCK_CLEAR,
3307                                           secondarySize );
3308         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3309         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3310     }
3313     sigc::connection *connection = new sigc::connection(
3314         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3315         );
3316     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3317     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3320 //########################
3321 //##     Pen/Pencil     ##
3322 //########################
3324 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3325 static Glib::ustring const
3326 freehand_tool_name(GObject *dataKludge)
3328     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3329     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3330              ? "/tools/freehand/pen"
3331              : "/tools/freehand/pencil" );
3334 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3336     gint mode = ege_select_one_action_get_active(act);
3338     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3339     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3341     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3343     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3344     // preparatory work here
3345     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3346         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3347         sp_pen_context_set_polyline_mode(pc);
3348     }
3351 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3353     /* Freehand mode toggle buttons */
3354     {
3355         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3356         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3357         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3359         {
3360             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3362             GtkTreeIter iter;
3363             gtk_list_store_append( model, &iter );
3364             gtk_list_store_set( model, &iter,
3365                                 0, _("Bezier"),
3366                                 1, _("Create regular Bezier path"),
3367                                 2, "bezier_mode",
3368                                 -1 );
3370             gtk_list_store_append( model, &iter );
3371             gtk_list_store_set( model, &iter,
3372                                 0, _("Spiro"),
3373                                 1, _("Create Spiro path"),
3374                                 2, "spiro_splines_mode",
3375                                 -1 );
3377             if (!tool_is_pencil) {
3378                 gtk_list_store_append( model, &iter );
3379                 gtk_list_store_set( model, &iter,
3380                                     0, _("Zigzag"),
3381                                     1, _("Create a sequence of straight line segments"),
3382                                     2, "polylines_mode",
3383                                     -1 );
3385                 gtk_list_store_append( model, &iter );
3386                 gtk_list_store_set( model, &iter,
3387                                     0, _("Paraxial"),
3388                                     1, _("Create a sequence of paraxial line segments"),
3389                                     2, "paraxial_lines_mode",
3390                                     -1 );
3391             }
3393             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3394                                                                 "FreehandModeActionPencil" :
3395                                                                 "FreehandModeActionPen",
3396                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3397             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3399             ege_select_one_action_set_appearance( act, "full" );
3400             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3401             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3402             ege_select_one_action_set_icon_column( act, 2 );
3403             ege_select_one_action_set_icon_size( act, secondarySize );
3404             ege_select_one_action_set_tooltip_column( act, 1  );
3406             ege_select_one_action_set_active( act, freehandMode);
3407             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3408         }
3409     }
3412 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3413     gint shape = ege_select_one_action_get_active( act );
3414     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3415     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3418 /**
3419  * \brief Generate the list of freehand advanced shape option entries.
3420  */
3421 GList * freehand_shape_dropdown_items_list() {
3422     GList *glist = NULL;
3424     glist = g_list_append (glist, _("None"));
3425     glist = g_list_append (glist, _("Triangle in"));
3426     glist = g_list_append (glist, _("Triangle out"));
3427     glist = g_list_append (glist, _("Ellipse"));
3428     glist = g_list_append (glist, _("From clipboard"));
3430     return glist;
3433 static void
3434 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3435     /*advanced shape options */
3436     {
3437         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3438         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3440         GList* items = 0;
3441         gint count = 0;
3442         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3443         {
3444             GtkTreeIter iter;
3445             gtk_list_store_append( model, &iter );
3446             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3447             count++;
3448         }
3449         g_list_free( items );
3450         items = 0;
3451         EgeSelectOneAction* act1 = ege_select_one_action_new(
3452             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3453             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3454         g_object_set( act1, "short_label", _("Shape:"), NULL );
3455         ege_select_one_action_set_appearance( act1, "compact" );
3456         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3457         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3458         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3459         g_object_set_data( holder, "shape_action", act1 );
3460     }
3463 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3465     sp_add_freehand_mode_toggle(mainActions, holder, false);
3466     freehand_add_advanced_shape_options(mainActions, holder, false);
3470 static void
3471 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3473     GtkWidget *tbl = GTK_WIDGET(obj);
3475     GtkAdjustment *adj;
3477     // fixme: make settable
3478     gdouble tolerance = 4;
3480     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3481     gtk_adjustment_set_value(adj, tolerance);
3482     gtk_adjustment_value_changed(adj);
3484     spinbutton_defocus(GTK_OBJECT(tbl));
3487 static void
3488 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3490     // quit if run by the attr_changed listener
3491     if (g_object_get_data( tbl, "freeze" )) {
3492         return;
3493     }
3494     // in turn, prevent listener from responding
3495     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3496     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3497     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3498     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3501 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3502 public:
3503     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3504     {
3505         g_object_set_data(_obj, "prefobserver", this);
3506     }
3507     virtual ~PencilToleranceObserver() {
3508         if (g_object_get_data(_obj, "prefobserver") == this) {
3509                 g_object_set_data(_obj, "prefobserver", NULL);
3510         }
3511     }
3512     virtual void notify(Inkscape::Preferences::Entry const &val) {
3513         GObject* tbl = _obj;
3514         if (g_object_get_data( tbl, "freeze" )) {
3515             return;
3516         }
3517         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3519         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3521         double v = val.getDouble(adj->value);
3522         gtk_adjustment_set_value(adj, v);
3523         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3524     }
3525 private:
3526     GObject *_obj;
3527 };
3530 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3532     sp_add_freehand_mode_toggle(mainActions, holder, true);
3534     EgeAdjustmentAction* eact = 0;
3536     /* Tolerance */
3537     {
3538         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3539         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3540         eact = create_adjustment_action( "PencilToleranceAction",
3541                                          _("Smoothing:"), _("Smoothing: "),
3542                  _("How much smoothing (simplifying) is applied to the line"),
3543                                          "/tools/freehand/pencil/tolerance",
3544                                          3.0,
3545                                          GTK_WIDGET(desktop->canvas), NULL,
3546                                          holder, TRUE, "altx-pencil",
3547                                          1, 100.0, 0.5, 0,
3548                                          labels, values, G_N_ELEMENTS(labels),
3549                                          sp_pencil_tb_tolerance_value_changed,
3550                                          1, 2);
3551         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3552         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3554         PencilToleranceObserver *obs =
3555             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
3556     }
3558     /* advanced shape options */
3559     freehand_add_advanced_shape_options(mainActions, holder, true);
3561     /* Reset */
3562     {
3563         InkAction* inky = ink_action_new( "PencilResetAction",
3564                                           _("Defaults"),
3565                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3566                                           GTK_STOCK_CLEAR,
3567                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3568         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3569         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3570     }
3572     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3577 //########################
3578 //##       Tweak        ##
3579 //########################
3581 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3583     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3584     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
3587 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3589     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3590     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
3593 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3595     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3596     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
3599 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3601     int mode = ege_select_one_action_get_active( act );
3602     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3603     prefs->setInt("/tools/tweak/mode", mode);
3605     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3606     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3607     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3608     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3609     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3610     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3611     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3612         if (doh) gtk_action_set_sensitive (doh, TRUE);
3613         if (dos) gtk_action_set_sensitive (dos, TRUE);
3614         if (dol) gtk_action_set_sensitive (dol, TRUE);
3615         if (doo) gtk_action_set_sensitive (doo, TRUE);
3616         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3617         if (fid) gtk_action_set_sensitive (fid, FALSE);
3618     } else {
3619         if (doh) gtk_action_set_sensitive (doh, FALSE);
3620         if (dos) gtk_action_set_sensitive (dos, FALSE);
3621         if (dol) gtk_action_set_sensitive (dol, FALSE);
3622         if (doo) gtk_action_set_sensitive (doo, FALSE);
3623         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3624         if (fid) gtk_action_set_sensitive (fid, TRUE);
3625     }
3628 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3630     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3631     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
3634 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3635     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3636     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
3638 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3639     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3640     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
3642 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3643     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3644     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
3646 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3647     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3648     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
3651 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3653     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3654     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3656     {
3657         /* Width */
3658         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3659         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3660         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3661                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3662                                                               "/tools/tweak/width", 15,
3663                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3664                                                               1, 100, 1.0, 0.0,
3665                                                               labels, values, G_N_ELEMENTS(labels),
3666                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3667         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3668         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3669         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3670     }
3673     {
3674         /* Force */
3675         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3676         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3677         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3678                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3679                                                               "/tools/tweak/force", 20,
3680                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3681                                                               1, 100, 1.0, 0.0,
3682                                                               labels, values, G_N_ELEMENTS(labels),
3683                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3684         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3685         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3686         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3687     }
3689     /* Mode */
3690     {
3691         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3693         GtkTreeIter iter;
3694         gtk_list_store_append( model, &iter );
3695         gtk_list_store_set( model, &iter,
3696                             0, _("Move mode"),
3697                             1, _("Move objects in any direction"),
3698                             2, "tweak_move_mode",
3699                             -1 );
3701         gtk_list_store_append( model, &iter );
3702         gtk_list_store_set( model, &iter,
3703                             0, _("Move in/out mode"),
3704                             1, _("Move objects towards cursor; with Shift from cursor"),
3705                             2, "tweak_move_mode_inout",
3706                             -1 );
3708         gtk_list_store_append( model, &iter );
3709         gtk_list_store_set( model, &iter,
3710                             0, _("Move jitter mode"),
3711                             1, _("Move objects in random directions"),
3712                             2, "tweak_move_mode_jitter",
3713                             -1 );
3715         gtk_list_store_append( model, &iter );
3716         gtk_list_store_set( model, &iter,
3717                             0, _("Scale mode"),
3718                             1, _("Scale objects, with Shift scale up"),
3719                             2, "tweak_scale_mode",
3720                             -1 );
3722         gtk_list_store_append( model, &iter );
3723         gtk_list_store_set( model, &iter,
3724                             0, _("Rotate mode"),
3725                             1, _("Rotate objects, with Shift counterclockwise"),
3726                             2, "tweak_rotate_mode",
3727                             -1 );
3729         gtk_list_store_append( model, &iter );
3730         gtk_list_store_set( model, &iter,
3731                             0, _("Duplicate/delete mode"),
3732                             1, _("Duplicate objects, with Shift delete"),
3733                             2, "tweak_moreless_mode",
3734                             -1 );
3736         gtk_list_store_append( model, &iter );
3737         gtk_list_store_set( model, &iter,
3738                             0, _("Push mode"),
3739                             1, _("Push parts of paths in any direction"),
3740                             2, "tweak_push_mode",
3741                             -1 );
3743         gtk_list_store_append( model, &iter );
3744         gtk_list_store_set( model, &iter,
3745                             0, _("Shrink/grow mode"),
3746                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
3747                             2, "tweak_shrink_mode",
3748                             -1 );
3750         gtk_list_store_append( model, &iter );
3751         gtk_list_store_set( model, &iter,
3752                             0, _("Attract/repel mode"),
3753                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
3754                             2, "tweak_attract_mode",
3755                             -1 );
3757         gtk_list_store_append( model, &iter );
3758         gtk_list_store_set( model, &iter,
3759                             0, _("Roughen mode"),
3760                             1, _("Roughen parts of paths"),
3761                             2, "tweak_roughen_mode",
3762                             -1 );
3764         gtk_list_store_append( model, &iter );
3765         gtk_list_store_set( model, &iter,
3766                             0, _("Color paint mode"),
3767                             1, _("Paint the tool's color upon selected objects"),
3768                             2, "tweak_colorpaint_mode",
3769                             -1 );
3771         gtk_list_store_append( model, &iter );
3772         gtk_list_store_set( model, &iter,
3773                             0, _("Color jitter mode"),
3774                             1, _("Jitter the colors of selected objects"),
3775                             2, "tweak_colorjitter_mode",
3776                             -1 );
3778         gtk_list_store_append( model, &iter );
3779         gtk_list_store_set( model, &iter,
3780                             0, _("Blur mode"),
3781                             1, _("Blur selected objects more; with Shift, blur less"),
3782                             2, "tweak_blur_mode",
3783                             -1 );
3786         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3787         g_object_set( act, "short_label", _("Mode:"), NULL );
3788         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3789         g_object_set_data( holder, "mode_action", act );
3791         ege_select_one_action_set_appearance( act, "full" );
3792         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3793         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3794         ege_select_one_action_set_icon_column( act, 2 );
3795         ege_select_one_action_set_icon_size( act, secondarySize );
3796         ege_select_one_action_set_tooltip_column( act, 1  );
3798         gint mode = prefs->getInt("/tools/tweak/mode", 0);
3799         ege_select_one_action_set_active( act, mode );
3800         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3802         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3803     }
3805     guint mode = prefs->getInt("/tools/tweak/mode", 0);
3807     {
3808         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3809         ege_output_action_set_use_markup( act, TRUE );
3810         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3811         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3812             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3813         g_object_set_data( holder, "tweak_channels_label", act);
3814     }
3816     {
3817         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3818                                                       _("Hue"),
3819                                                       _("In color mode, act on objects' hue"),
3820                                                       NULL,
3821                                                       Inkscape::ICON_SIZE_DECORATION );
3822         //TRANSLATORS:  "H" here stands for hue
3823         g_object_set( act, "short_label", _("H"), NULL );
3824         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3825         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3826         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
3827         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3828             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3829         g_object_set_data( holder, "tweak_doh", act);
3830     }
3831     {
3832         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3833                                                       _("Saturation"),
3834                                                       _("In color mode, act on objects' saturation"),
3835                                                       NULL,
3836                                                       Inkscape::ICON_SIZE_DECORATION );
3837         //TRANSLATORS: "S" here stands for Saturation
3838         g_object_set( act, "short_label", _("S"), NULL );
3839         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3840         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3841         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
3842         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3843             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3844         g_object_set_data( holder, "tweak_dos", act );
3845     }
3846     {
3847         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3848                                                       _("Lightness"),
3849                                                       _("In color mode, act on objects' lightness"),
3850                                                       NULL,
3851                                                       Inkscape::ICON_SIZE_DECORATION );
3852         //TRANSLATORS: "L" here stands for Lightness
3853         g_object_set( act, "short_label", _("L"), NULL );
3854         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3855         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3856         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
3857         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3858             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3859         g_object_set_data( holder, "tweak_dol", act );
3860     }
3861     {
3862         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3863                                                       _("Opacity"),
3864                                                       _("In color mode, act on objects' opacity"),
3865                                                       NULL,
3866                                                       Inkscape::ICON_SIZE_DECORATION );
3867         //TRANSLATORS: "O" here stands for Opacity
3868         g_object_set( act, "short_label", _("O"), NULL );
3869         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3870         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3871         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
3872         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3873             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3874         g_object_set_data( holder, "tweak_doo", act );
3875     }
3877     {   /* Fidelity */
3878         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3879         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3880         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3881                                                               _("Fidelity"), _("Fidelity:"),
3882                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3883                                                               "/tools/tweak/fidelity", 50,
3884                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3885                                                               1, 100, 1.0, 10.0,
3886                                                               labels, values, G_N_ELEMENTS(labels),
3887                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3888         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3889         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3890         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3891             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3892         g_object_set_data( holder, "tweak_fidelity", eact );
3893     }
3896     /* Use Pressure button */
3897     {
3898         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3899                                                       _("Pressure"),
3900                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3901                                                       "use_pressure",
3902                                                       Inkscape::ICON_SIZE_DECORATION );
3903         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3904         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3905         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
3906     }
3911 //########################
3912 //##     Calligraphy    ##
3913 //########################
3914 static void update_presets_list (GObject *tbl)
3916     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3917     if (g_object_get_data(tbl, "presets_blocked"))
3918         return;
3920     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3921     if (!sel) {
3922         // WTF!? This will cause a segfault if ever reached
3923         //ege_select_one_action_set_active(sel, 0);
3924         return;
3925     }
3927     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
3929     int ege_index = 1;
3930     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
3931         bool match = true;
3932         
3933         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
3934         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
3935             Glib::ustring entry_name = j->getEntryName();
3936             if (entry_name == "id" || entry_name == "name") continue;
3938             void *widget = g_object_get_data(tbl, entry_name.data());
3939             if (widget) {
3940                 if (GTK_IS_ADJUSTMENT(widget)) {
3941                     double v = j->getDouble();
3942                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3943                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3944                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3945                         match = false;
3946                         break;
3947                     }
3948                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3949                     bool v = j->getBool();
3950                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3951                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3952                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
3953                         match = false;
3954                         break;
3955                     }
3956                 }
3957             }
3958         }
3959         
3960         if (match) {
3961             // newly added item is at the same index as the
3962             // save command, so we need to change twice for it to take effect
3963             ege_select_one_action_set_active(sel, 0);
3964             ege_select_one_action_set_active(sel, ege_index); // one-based index
3965             return;
3966         }
3967     }
3969     // no match found
3970     ege_select_one_action_set_active(sel, 0);
3973 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3975     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3976     prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
3977     update_presets_list(tbl);
3980 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3982     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3983     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
3984     update_presets_list(tbl);
3987 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3989     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3990     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
3991     update_presets_list(tbl);
3994 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3996     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3997     prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
3998     update_presets_list(tbl);
4001 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4003     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4004     prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4005     update_presets_list(tbl);
4008 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4010     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4011     prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4012     update_presets_list(tbl);
4015 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4017     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4018     prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4019     update_presets_list(tbl);
4022 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4024     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4025     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4026     update_presets_list(tbl);
4029 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4031     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4032     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4033     update_presets_list(tbl);
4036 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4038     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4039     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4040     update_presets_list(tbl);
4043 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4045     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4046     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4047     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4048     update_presets_list(tbl);
4049     if (calligraphy_angle )
4050         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4054 static gchar const *const widget_names[] = {
4055     "width",
4056     "mass",
4057     "wiggle",
4058     "angle",
4059     "thinning",
4060     "tremor",
4061     "flatness",
4062     "cap_rounding",
4063     "usepressure",
4064     "tracebackground",
4065     "usetilt"
4066 };
4069 static void sp_dcc_build_presets_list(GObject *tbl)
4071     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4073     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4074     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4075     gtk_list_store_clear (model);
4077     {
4078         GtkTreeIter iter;
4079         gtk_list_store_append( model, &iter );
4080         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4081     }
4082     
4083     // iterate over all presets to populate the list
4084     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4085     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4086     int ii=1;
4087     
4088     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4089         GtkTreeIter iter;
4090         Glib::ustring preset_name = prefs->getString(*i + "/name");
4091         gtk_list_store_append( model, &iter );
4092         gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4093     }
4095     {
4096         GtkTreeIter iter;
4097         gtk_list_store_append( model, &iter );
4098         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4099         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4100     }
4102     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4104     update_presets_list (tbl);
4107 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4109     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4110     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4111     if (! desktop) return;
4113     if (g_object_get_data(tbl, "presets_blocked"))
4114         return;
4116     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4117     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4118         // dialog cancelled
4119         update_presets_list (tbl);
4120         return;
4121     }
4122     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4124     if (profile_name.empty()) {
4125         // empty name entered
4126         update_presets_list (tbl);
4127         return;
4128     }
4130     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4131     
4132     // If there's a preset with the given name, find it and set save_path appropriately
4133     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4134     int total_presets = presets.size();
4135     int new_index = -1;
4136     Glib::ustring save_path; // profile pref path without a trailing slash
4137     
4138     int temp_index = 0;
4139     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4140         Glib::ustring name = prefs->getString(*i + "/name");
4141         if (!name.empty() && profile_name == name) {
4142             new_index = temp_index;
4143             save_path = *i;
4144             break;
4145         }
4146     }
4148     if (new_index == -1) {
4149         // no preset with this name, create
4150         new_index = total_presets + 1;
4151         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4152         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4153         g_free(profile_id);
4154     }
4156     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4157         gchar const *const widget_name = widget_names[i];
4158         void *widget = g_object_get_data(tbl, widget_name);
4159         if (widget) {
4160             if (GTK_IS_ADJUSTMENT(widget)) {
4161                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4162                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4163                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4164             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4165                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4166                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4167                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4168             } else {
4169                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4170             }
4171         } else {
4172             g_warning("Bad key when writing preset: %s\n", widget_name);
4173         }
4174     }
4175     prefs->setString(save_path + "/name", profile_name);
4177     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4178     sp_dcc_build_presets_list (tbl);
4182 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4184     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4186     gint preset_index = ege_select_one_action_get_active( act );
4187     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4188     // even when the preset is not changed. It would be good to replace it with something more
4189     // modern. Index 0 means "No preset", so we don't do anything.
4190     if (preset_index == 0) return;
4192     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4194     if (preset_index == save_presets_index) {
4195         // this is the Save command
4196         sp_dcc_save_profile(NULL, tbl);
4197         return;
4198     }
4200     if (g_object_get_data(tbl, "presets_blocked"))
4201         return;
4203     // preset_index is one-based so we subtract 1
4204     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4205     Glib::ustring preset_path = presets.at(preset_index - 1);
4207     if (!preset_path.empty()) {
4208         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
4209         
4210         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4212         // Shouldn't this be std::map?
4213         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4214             Glib::ustring entry_name = i->getEntryName();
4215             if (entry_name == "id" || entry_name == "name") continue;
4216             void *widget = g_object_get_data(tbl, entry_name.data());
4217             if (widget) {
4218                 if (GTK_IS_ADJUSTMENT(widget)) {
4219                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4220                     gtk_adjustment_set_value(adj, i->getDouble());
4221                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4222                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4223                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4224                     gtk_toggle_action_set_active(toggle, i->getBool());
4225                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4226                 } else {
4227                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4228                 }
4229             } else {
4230                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4231             }
4232         }
4233         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4234     }
4238 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4240     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4241     {
4242         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4244         EgeAdjustmentAction* calligraphy_angle = 0;
4246         {
4247         /* Width */
4248         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4249         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4250         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4251                                                               _("Pen Width"), _("Width:"),
4252                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4253                                                               "/tools/calligraphic/width", 15,
4254                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4255                                                               1, 100, 1.0, 0.0,
4256                                                               labels, values, G_N_ELEMENTS(labels),
4257                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4258         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4259         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4260         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4261         }
4263         {
4264         /* Thinning */
4265             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4266             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4267         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4268                                                               _("Stroke Thinning"), _("Thinning:"),
4269                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4270                                                               "/tools/calligraphic/thinning", 10,
4271                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4272                                                               -100, 100, 1, 0.1,
4273                                                               labels, values, G_N_ELEMENTS(labels),
4274                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4275         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4276         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4277         }
4279         {
4280         /* Angle */
4281         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4282         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4283         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4284                                                               _("Pen Angle"), _("Angle:"),
4285                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4286                                                               "/tools/calligraphic/angle", 30,
4287                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4288                                                               -90.0, 90.0, 1.0, 10.0,
4289                                                               labels, values, G_N_ELEMENTS(labels),
4290                                                               sp_ddc_angle_value_changed, 1, 0 );
4291         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4292         g_object_set_data( holder, "angle_action", eact );
4293         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4294         calligraphy_angle = eact;
4295         }
4297         {
4298         /* Fixation */
4299             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4300         gdouble values[] = {0, 20, 40, 60, 90, 100};
4301         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4302                                                               _("Fixation"), _("Fixation:"),
4303                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4304                                                               "/tools/calligraphic/flatness", 90,
4305                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4306                                                               0.0, 100, 1.0, 10.0,
4307                                                               labels, values, G_N_ELEMENTS(labels),
4308                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4309         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4310         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4311         }
4313         {
4314         /* Cap Rounding */
4315             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4316         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4317         // TRANSLATORS: "cap" means "end" (both start and finish) here
4318         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4319                                                               _("Cap rounding"), _("Caps:"),
4320                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4321                                                               "/tools/calligraphic/cap_rounding", 0.0,
4322                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4323                                                               0.0, 5.0, 0.01, 0.1,
4324                                                               labels, values, G_N_ELEMENTS(labels),
4325                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4326         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4327         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4328         }
4330         {
4331         /* Tremor */
4332             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4333         gdouble values[] = {0, 10, 20, 40, 60, 100};
4334         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4335                                                               _("Stroke Tremor"), _("Tremor:"),
4336                                                               _("Increase to make strokes rugged and trembling"),
4337                                                               "/tools/calligraphic/tremor", 0.0,
4338                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4339                                                               0.0, 100, 1, 0.0,
4340                                                               labels, values, G_N_ELEMENTS(labels),
4341                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4343         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4344         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4345         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4346         }
4348         {
4349         /* Wiggle */
4350         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4351         gdouble values[] = {0, 20, 40, 60, 100};
4352         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4353                                                               _("Pen Wiggle"), _("Wiggle:"),
4354                                                               _("Increase to make the pen waver and wiggle"),
4355                                                               "/tools/calligraphic/wiggle", 0.0,
4356                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4357                                                               0.0, 100, 1, 0.0,
4358                                                               labels, values, G_N_ELEMENTS(labels),
4359                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4360         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4361         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4362         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4363         }
4365         {
4366         /* Mass */
4367             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4368         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4369         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4370                                                               _("Pen Mass"), _("Mass:"),
4371                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4372                                                               "/tools/calligraphic/mass", 2.0,
4373                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4374                                                               0.0, 100, 1, 0.0,
4375                                                               labels, values, G_N_ELEMENTS(labels),
4376                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4377         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4378         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4379         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4380         }
4383         /* Trace Background button */
4384         {
4385             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4386                                                           _("Trace Background"),
4387                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4388                                                           "trace_background",
4389                                                           Inkscape::ICON_SIZE_DECORATION );
4390             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4391             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4392             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4393             g_object_set_data( holder, "tracebackground", act );
4394         }
4396         /* Use Pressure button */
4397         {
4398             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4399                                                           _("Pressure"),
4400                                                           _("Use the pressure of the input device to alter the width of the pen"),
4401                                                           "use_pressure",
4402                                                           Inkscape::ICON_SIZE_DECORATION );
4403             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4404             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4405             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4406             g_object_set_data( holder, "usepressure", act );
4407         }
4409         /* Use Tilt button */
4410         {
4411             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4412                                                           _("Tilt"),
4413                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4414                                                           "use_tilt",
4415                                                           Inkscape::ICON_SIZE_DECORATION );
4416             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4417             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4418             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4419             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4420             g_object_set_data( holder, "usetilt", act );
4421         }
4423         /*calligraphic profile */
4424         {
4425             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4426             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4427             ege_select_one_action_set_appearance (act1, "compact");
4428             g_object_set_data (holder, "profile_selector", act1 );
4430             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4432             sp_dcc_build_presets_list (holder);
4434             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4435             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4436         }
4437     }
4441 //########################
4442 //##    Circle / Arc    ##
4443 //########################
4445 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4447     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4448     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4450     if (v1 == 0 && v2 == 0) {
4451         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4452             gtk_action_set_sensitive( ocb, FALSE );
4453             gtk_action_set_sensitive( make_whole, FALSE );
4454         }
4455     } else {
4456         gtk_action_set_sensitive( ocb, TRUE );
4457         gtk_action_set_sensitive( make_whole, TRUE );
4458     }
4461 static void
4462 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4464     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4466     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4467         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4468         prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4469     }
4471     // quit if run by the attr_changed listener
4472     if (g_object_get_data( tbl, "freeze" )) {
4473         return;
4474     }
4476     // in turn, prevent listener from responding
4477     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4479     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4481     bool modmade = false;
4482     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4483          items != NULL;
4484          items = items->next)
4485     {
4486         SPItem *item = SP_ITEM(items->data);
4488         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4490             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4491             SPArc *arc = SP_ARC(item);
4493             if (!strcmp(value_name, "start"))
4494                 ge->start = (adj->value * M_PI)/ 180;
4495             else
4496                 ge->end = (adj->value * M_PI)/ 180;
4498             sp_genericellipse_normalize(ge);
4499             ((SPObject *)arc)->updateRepr();
4500             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4502             modmade = true;
4503         }
4504     }
4506     g_free(namespaced_name);
4508     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4510     sp_arctb_sensitivize( tbl, adj->value, other->value );
4512     if (modmade) {
4513         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4514                                    _("Arc: Change start/end"));
4515     }
4517     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4521 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4523     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4526 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4528     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4532 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4534     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4535     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4536         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4537         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4538     }
4540     // quit if run by the attr_changed listener
4541     if (g_object_get_data( tbl, "freeze" )) {
4542         return;
4543     }
4545     // in turn, prevent listener from responding
4546     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4548     bool modmade = false;
4550     if ( ege_select_one_action_get_active(act) != 0 ) {
4551         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4552              items != NULL;
4553              items = items->next)
4554         {
4555             if (SP_IS_ARC((SPItem *) items->data)) {
4556                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4557                 repr->setAttribute("sodipodi:open", "true");
4558                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4559                 modmade = true;
4560             }
4561         }
4562     } else {
4563         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4564              items != NULL;
4565              items = items->next)
4566         {
4567             if (SP_IS_ARC((SPItem *) items->data))    {
4568                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4569                 repr->setAttribute("sodipodi:open", NULL);
4570                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4571                 modmade = true;
4572             }
4573         }
4574     }
4576     if (modmade) {
4577         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4578                                    _("Arc: Change open/closed"));
4579     }
4581     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4584 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4586     GtkAdjustment *adj;
4587     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4588     gtk_adjustment_set_value(adj, 0.0);
4589     gtk_adjustment_value_changed(adj);
4591     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4592     gtk_adjustment_set_value(adj, 0.0);
4593     gtk_adjustment_value_changed(adj);
4595     spinbutton_defocus( GTK_OBJECT(obj) );
4598 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4599                                       gchar const */*old_value*/, gchar const */*new_value*/,
4600                                       bool /*is_interactive*/, gpointer data)
4602     GObject *tbl = G_OBJECT(data);
4604     // quit if run by the _changed callbacks
4605     if (g_object_get_data( tbl, "freeze" )) {
4606         return;
4607     }
4609     // in turn, prevent callbacks from responding
4610     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4612     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4613     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4615     GtkAdjustment *adj1,*adj2;
4616     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4617     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4618     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4619     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4621     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4623     char const *openstr = NULL;
4624     openstr = repr->attribute("sodipodi:open");
4625     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4627     if (openstr) {
4628         ege_select_one_action_set_active( ocb, 1 );
4629     } else {
4630         ege_select_one_action_set_active( ocb, 0 );
4631     }
4633     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4636 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4637     NULL, /* child_added */
4638     NULL, /* child_removed */
4639     arc_tb_event_attr_changed,
4640     NULL, /* content_changed */
4641     NULL  /* order_changed */
4642 };
4645 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4647     int n_selected = 0;
4648     Inkscape::XML::Node *repr = NULL;
4650     purge_repr_listener( tbl, tbl );
4652     for (GSList const *items = selection->itemList();
4653          items != NULL;
4654          items = items->next)
4655     {
4656         if (SP_IS_ARC((SPItem *) items->data)) {
4657             n_selected++;
4658             repr = SP_OBJECT_REPR((SPItem *) items->data);
4659         }
4660     }
4662     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4664     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4665     if (n_selected == 0) {
4666         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4667     } else if (n_selected == 1) {
4668         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4669         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4671         if (repr) {
4672             g_object_set_data( tbl, "repr", repr );
4673             Inkscape::GC::anchor(repr);
4674             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4675             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4676         }
4677     } else {
4678         // FIXME: implement averaging of all parameters for multiple selected
4679         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4680         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4681         sp_arctb_sensitivize( tbl, 1, 0 );
4682     }
4686 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4688     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4690     EgeAdjustmentAction* eact = 0;
4691     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4694     {
4695         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4696         ege_output_action_set_use_markup( act, TRUE );
4697         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4698         g_object_set_data( holder, "mode_action", act );
4699     }
4701     /* Start */
4702     {
4703         eact = create_adjustment_action( "ArcStartAction",
4704                                          _("Start"), _("Start:"),
4705                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4706                                          "/tools/shapes/arc/start", 0.0,
4707                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4708                                          -360.0, 360.0, 1.0, 10.0,
4709                                          0, 0, 0,
4710                                          sp_arctb_start_value_changed);
4711         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4712     }
4714     /* End */
4715     {
4716         eact = create_adjustment_action( "ArcEndAction",
4717                                          _("End"), _("End:"),
4718                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4719                                          "/tools/shapes/arc/end", 0.0,
4720                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4721                                          -360.0, 360.0, 1.0, 10.0,
4722                                          0, 0, 0,
4723                                          sp_arctb_end_value_changed);
4724         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4725     }
4727     /* Segments / Pie checkbox */
4728     {
4729         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4731         GtkTreeIter iter;
4732         gtk_list_store_append( model, &iter );
4733         gtk_list_store_set( model, &iter,
4734                             0, _("Closed arc"),
4735                             1, _("Switch to segment (closed shape with two radii)"),
4736                             2, "circle_closed_arc",
4737                             -1 );
4739         gtk_list_store_append( model, &iter );
4740         gtk_list_store_set( model, &iter,
4741                             0, _("Open Arc"),
4742                             1, _("Switch to arc (unclosed shape)"),
4743                             2, "circle_open_arc",
4744                             -1 );
4746         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4747         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4748         g_object_set_data( holder, "open_action", act );
4750         ege_select_one_action_set_appearance( act, "full" );
4751         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4752         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4753         ege_select_one_action_set_icon_column( act, 2 );
4754         ege_select_one_action_set_icon_size( act, secondarySize );
4755         ege_select_one_action_set_tooltip_column( act, 1  );
4757         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
4758         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4759         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4760     }
4762     /* Make Whole */
4763     {
4764         InkAction* inky = ink_action_new( "ArcResetAction",
4765                                           _("Make whole"),
4766                                           _("Make the shape a whole ellipse, not arc or segment"),
4767                                           "reset_circle",
4768                                           secondarySize );
4769         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4770         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4771         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4772         g_object_set_data( holder, "make_whole", inky );
4773     }
4775     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4776     // sensitivize make whole and open checkbox
4777     {
4778         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4779         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4780         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4781     }
4784     sigc::connection *connection = new sigc::connection(
4785         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4786         );
4787     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4788     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4794 // toggle button callbacks and updaters
4796 //########################
4797 //##      Dropper       ##
4798 //########################
4800 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4801     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4802     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
4803     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4804     if ( set_action ) {
4805         if ( gtk_toggle_action_get_active( act ) ) {
4806             gtk_action_set_sensitive( set_action, TRUE );
4807         } else {
4808             gtk_action_set_sensitive( set_action, FALSE );
4809         }
4810     }
4812     spinbutton_defocus(GTK_OBJECT(tbl));
4815 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4816     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4817     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
4818     spinbutton_defocus(GTK_OBJECT(tbl));
4822 /**
4823  * Dropper auxiliary toolbar construction and setup.
4824  *
4825  * TODO: Would like to add swatch of current color.
4826  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4827  *       can drag and drop places. Will provide a nice mixing palette.
4828  */
4829 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4831     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4832     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
4834     {
4835         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4836         ege_output_action_set_use_markup( act, TRUE );
4837         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4838     }
4840     {
4841         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4842                                                       _("Pick opacity"),
4843                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4844                                                       NULL,
4845                                                       Inkscape::ICON_SIZE_DECORATION );
4846         g_object_set( act, "short_label", _("Pick"), NULL );
4847         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4848         g_object_set_data( holder, "pick_action", act );
4849         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4850         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4851     }
4853     {
4854         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4855                                                       _("Assign opacity"),
4856                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4857                                                       NULL,
4858                                                       Inkscape::ICON_SIZE_DECORATION );
4859         g_object_set( act, "short_label", _("Assign"), NULL );
4860         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4861         g_object_set_data( holder, "set_action", act );
4862         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
4863         // make sure it's disabled if we're not picking alpha
4864         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4865         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4866     }
4870 //########################
4871 //##      LPETool       ##
4872 //########################
4874 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4876 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4877 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4879     using namespace Inkscape::LivePathEffect;
4881     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4882     SPEventContext *ec = desktop->event_context;
4883     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4884         return;
4885     }
4887     // only take action if run by the attr_changed listener
4888     if (!g_object_get_data(tbl, "freeze")) {
4889         // in turn, prevent listener from responding
4890         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4892         gint mode = ege_select_one_action_get_active(act);
4893         EffectType type = lpesubtools[mode];
4895         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4896         bool success = lpetool_try_construction(lc, type);
4897         if (success) {
4898             // since the construction was already performed, we set the state back to inactive
4899             ege_select_one_action_set_active(act, 0);
4900             mode = 0;
4901         } else {
4902             // switch to the chosen subtool
4903             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4904         }
4906         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4907             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4908             prefs->setInt( "/tools/lpetool/mode", mode );
4909         }
4911         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4912     }
4915 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
4917     SPEventContext *ec = selection->desktop()->event_context;
4918     if (!SP_IS_LPETOOL_CONTEXT(ec))
4919         return;
4921     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4924 void
4925 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4927     using namespace Inkscape::LivePathEffect;
4928     SPEventContext *ec = selection->desktop()->event_context;
4929     if (!SP_IS_LPETOOL_CONTEXT(ec))
4930         return;
4931     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4933     lpetool_delete_measuring_items(lc);
4934     lpetool_create_measuring_items(lc, selection);
4936     // activate line segment combo box if a single item with LPELineSegment is selected
4937     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4938     SPItem *item = selection->singleItem();
4939     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4940         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4941         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4942         if (lpe && lpe->effectType() == LINE_SEGMENT) {
4943             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4944             g_object_set_data(tbl, "currentlpe", lpe);
4945             g_object_set_data(tbl, "currentlpeitem", lpeitem);
4946             gtk_action_set_sensitive(w, TRUE);
4947             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4948         } else {
4949             g_object_set_data(tbl, "currentlpe", NULL);
4950             g_object_set_data(tbl, "currentlpeitem", NULL);
4951             gtk_action_set_sensitive(w, FALSE);
4952         }
4953     } else {
4954         g_object_set_data(tbl, "currentlpe", NULL);
4955         g_object_set_data(tbl, "currentlpeitem", NULL);
4956         gtk_action_set_sensitive(w, FALSE);
4957     }
4960 static void
4961 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4962     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4963     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4965     bool show = gtk_toggle_action_get_active( act );
4966     prefs->setBool("/tools/lpetool/show_bbox",  show);
4968     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4969         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4970         lpetool_context_reset_limiting_bbox(lc);
4971     }
4974 static void
4975 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4976     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4977     if (!tools_isactive(desktop, TOOLS_LPETOOL))
4978         return;
4980     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4981     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4982     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4983         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4984         bool show = gtk_toggle_action_get_active( act );
4985         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
4986         lpetool_show_measuring_info(lc, show);
4987         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4988     }
4991 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
4992     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4993     SPUnit const *unit = tracker->getActiveUnit();
4994     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4995     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
4997     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4998     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4999         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5000         lpetool_delete_measuring_items(lc);
5001         lpetool_create_measuring_items(lc);
5002     }
5005 static void
5006 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5007     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5008     Inkscape::Selection *selection = desktop->selection;
5010     boost::optional<Geom::Rect> bbox = selection->bounds();
5012     if (bbox) {
5013         Geom::Point A(bbox->min());
5014         Geom::Point B(bbox->max());
5016         A *= desktop->doc2dt();
5017         B *= desktop->doc2dt();
5019         // TODO: should we provide a way to store points in prefs?
5020         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5021         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5022         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5023         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5024         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5026         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5027     }
5029     gtk_toggle_action_set_active(act, false);
5032 static void
5033 sp_line_segment_build_list(GObject *tbl)
5035     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5037     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5038     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5039     gtk_list_store_clear (model);
5041     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5042     {
5043         GtkTreeIter iter;
5044         gtk_list_store_append( model, &iter );
5045         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5046         gtk_list_store_append( model, &iter );
5047         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5048         gtk_list_store_append( model, &iter );
5049         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5050         gtk_list_store_append( model, &iter );
5051         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5052     }
5054     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5057 static void
5058 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5059     using namespace Inkscape::LivePathEffect;
5061     // quit if run by the attr_changed listener
5062     if (g_object_get_data(tbl, "freeze")) {
5063         return;
5064     }
5066     // in turn, prevent listener from responding
5067     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5069     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5070     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5071     if (lpeitem) {
5072         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5073         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5074         sp_lpe_item_update_patheffect(lpeitem, true, true);
5075     }
5077     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5080 static void
5081 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5082     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5084     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5085         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5086     }
5087     gtk_toggle_action_set_active(act, false);
5090 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5092     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5093     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5094     g_object_set_data(holder, "tracker", tracker);
5095     SPUnit const *unit = tracker->getActiveUnit();
5097     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5098     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5100     /** Automatically create a list of LPEs that get added to the toolbar **/
5101     {
5102         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5104         GtkTreeIter iter;
5106         // the first toggle button represents the state that no subtool is active (remove this when
5107         // this can be modeled by EgeSelectOneAction or some other action)
5108         gtk_list_store_append( model, &iter );
5109         gtk_list_store_set( model, &iter,
5110                             0, _("All inactive"),
5111                             1, _("No geometric tool is active"),
5112                             2, _("all_inactive"),
5113                             -1 );
5115         Inkscape::LivePathEffect::EffectType type;
5116         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5117             type =  lpesubtools[i];
5118             gtk_list_store_append( model, &iter );
5119             gtk_list_store_set( model, &iter,
5120                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5121                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5122                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5123                                 -1 );
5124         }
5126         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5127         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5128         g_object_set_data( holder, "lpetool_mode_action", act );
5130         ege_select_one_action_set_appearance( act, "full" );
5131         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5132         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5133         ege_select_one_action_set_icon_column( act, 2 );
5134         ege_select_one_action_set_tooltip_column( act, 1  );
5136         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5137         ege_select_one_action_set_active( act, lpeToolMode );
5138         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5139     }
5141     /* Show limiting bounding box */
5142     {
5143         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5144                                                       _("Show limiting bounding box"),
5145                                                       _("Show bounding box (used to cut infinite lines)"),
5146                                                       "lpetool_show_bbox",
5147                                                       Inkscape::ICON_SIZE_DECORATION );
5148         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5149         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5150         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5151     }
5153     /* Set limiting bounding box to bbox of current selection */
5154     {
5155         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5156                                                       _("Get limiting bounding box from selection"),
5157                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5158                                                       "lpetool_set_bbox",
5159                                                       Inkscape::ICON_SIZE_DECORATION );
5160         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5161         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5162         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5163     }
5166     /* Combo box to choose line segment type */
5167     {
5168         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5169         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5170         ege_select_one_action_set_appearance (act, "compact");
5171         g_object_set_data (holder, "lpetool_line_segment_action", act );
5173         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5175         sp_line_segment_build_list (holder);
5177         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5178         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5179         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5180     }
5182     /* Display measuring info for selected items */
5183     {
5184         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5185                                                       _("Display measuring info"),
5186                                                       _("Display measuring info for selected items"),
5187                                                       "lpetool_measuring_info",
5188                                                       Inkscape::ICON_SIZE_DECORATION );
5189         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5190         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5191         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5192     }
5194     // add the units menu
5195     {
5196         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5197         gtk_action_group_add_action( mainActions, act );
5198         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5199         g_object_set_data(holder, "lpetool_units_action", act);
5200         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5201     }
5203     /* Open LPE dialog (to adapt parameters numerically) */
5204     {
5205         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5206                                                       _("Open LPE dialog"),
5207                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5208                                                       "lpetool_open_lpe_dialog",
5209                                                       Inkscape::ICON_SIZE_DECORATION );
5210         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5211         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5212         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5213     }
5215     //watch selection
5216     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5218     sigc::connection *c_selection_modified =
5219         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5220                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5221     pool->add_connection ("selection-modified", c_selection_modified);
5223     sigc::connection *c_selection_changed =
5224         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5225                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5226     pool->add_connection ("selection-changed", c_selection_changed);
5229 //########################
5230 //##       Eraser       ##
5231 //########################
5233 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5235     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5236     prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5237     update_presets_list(tbl);
5240 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5242     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5243     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5244     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5245         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5246         prefs->setBool( "/tools/eraser/mode", eraserMode );
5247     }
5249     // only take action if run by the attr_changed listener
5250     if (!g_object_get_data( tbl, "freeze" )) {
5251         // in turn, prevent listener from responding
5252         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5254         if ( eraserMode != 0 ) {
5255         } else {
5256         }
5257         // TODO finish implementation
5259         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5260     }
5263 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5265     {
5266         /* Width */
5267         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5268         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5269         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5270                                                               _("Pen Width"), _("Width:"),
5271                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5272                                                               "/tools/eraser/width", 15,
5273                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5274                                                               1, 100, 1.0, 0.0,
5275                                                               labels, values, G_N_ELEMENTS(labels),
5276                                                               sp_erc_width_value_changed,  0.01, 0, 100 );
5277         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5278         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5279         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5280     }
5282     {
5283         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5285         GtkTreeIter iter;
5286         gtk_list_store_append( model, &iter );
5287         gtk_list_store_set( model, &iter,
5288                             0, _("Delete"),
5289                             1, _("Delete objects touched by the eraser"),
5290                             2, "delete_object",
5291                             -1 );
5293         gtk_list_store_append( model, &iter );
5294         gtk_list_store_set( model, &iter,
5295                             0, _("Cut"),
5296                             1, _("Cut out from objects"),
5297                             2, "difference",
5298                             -1 );
5300         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5301         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5302         g_object_set_data( holder, "eraser_mode_action", act );
5304         ege_select_one_action_set_appearance( act, "full" );
5305         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5306         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5307         ege_select_one_action_set_icon_column( act, 2 );
5308         ege_select_one_action_set_tooltip_column( act, 1  );
5310         /// @todo Convert to boolean?
5311         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5312         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5313         ege_select_one_action_set_active( act, eraserMode );
5314         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5315     }
5319 //########################
5320 //##    Text Toolbox    ##
5321 //########################
5322 /*
5323 static void
5324 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5326     //Call back for letter sizing spinbutton
5329 static void
5330 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5332     //Call back for line height spinbutton
5335 static void
5336 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5338     //Call back for horizontal kerning spinbutton
5341 static void
5342 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5344     //Call back for vertical kerning spinbutton
5347 static void
5348 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5350     //Call back for letter rotation spinbutton
5351 }*/
5353 namespace {
5355 bool popdown_visible = false;
5356 bool popdown_hasfocus = false;
5358 void
5359 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5361     SPStyle *query =
5362         sp_style_new (SP_ACTIVE_DOCUMENT);
5364 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5366     int result_family =
5367         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5369     int result_style =
5370         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5372     int result_numbers =
5373         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5375     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5377     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5378     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5379         // there are no texts in selection, read from prefs
5380         
5381             sp_style_read_from_prefs(query, "/tools/text");
5382             
5383             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5384             // do not reset the toolbar style from prefs if we already did it last time
5385             sp_style_unref(query);
5386             return;
5387         }
5388         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5389     } else {
5390         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5391     }
5393     if (query->text)
5394     {
5395         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5396             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5397             gtk_entry_set_text (GTK_ENTRY (entry), "");
5399         } else if (query->text->font_specification.value || query->text->font_family.value) {
5401             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5403             // Get the font that corresponds
5404             Glib::ustring familyName;
5406             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5407             if (font) {
5408                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5409                 font->Unref();
5410                 font = NULL;
5411             }
5413             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5415             Gtk::TreePath path;
5416             try {
5417                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5418             } catch (...) {
5419                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5420                 sp_style_unref(query);
5421                 return;
5422             }
5424             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5425             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5427             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5429             gtk_tree_selection_select_path (tselection, path.gobj());
5430             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5432             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5433         }
5435         //Size
5436         {
5437             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5438             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5439             g_object_set_data(tbl, "size-block", gpointer(1));
5440             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5441             g_object_set_data(tbl, "size-block", gpointer(0));
5442             g_free(str);
5443         }
5445         //Anchor
5446         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5447         {
5448             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5449             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5450             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5451             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5452         }
5453         else
5454         {
5455             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5456             {
5457                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5458                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5459                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5460                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5461             }
5462             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5463             {
5464                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5465                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5466                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5467                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5468             }
5469             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5470             {
5471                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5472                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5473                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5474                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5475             }
5476         }
5478         //Style
5479         {
5480             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5482             gboolean active = gtk_toggle_button_get_active (button);
5483             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5485             if (active != check)
5486             {
5487                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5488                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5489                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5490             }
5491         }
5493         {
5494             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5496             gboolean active = gtk_toggle_button_get_active (button);
5497             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5499             if (active != check)
5500             {
5501                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5502                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5503                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5504             }
5505         }
5507         //Orientation
5508         //locking both buttons, changing one affect all group (both)
5509         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5510         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5512         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5513         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5515         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5516         {
5517             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5518         }
5519         else
5520         {
5521             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5522         }
5523         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5524         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5525     }
5527     sp_style_unref(query);
5530 void
5531 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5533     sp_text_toolbox_selection_changed (selection, tbl);
5536 void
5537 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5539     sp_text_toolbox_selection_changed (NULL, tbl);
5542 void
5543 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5544                                 GObject             *tbl)
5546     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5547     GtkTreeModel *model = 0;
5548     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5549     GtkTreeIter   iter;
5550     char         *family = 0;
5552     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5553     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5555     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5556         return;
5557     }
5559     gtk_tree_model_get (model, &iter, 0, &family, -1);
5561     if (g_object_get_data (G_OBJECT (selection), "block"))
5562     {
5563         gtk_entry_set_text (GTK_ENTRY (entry), family);
5564         return;
5565     }
5567     gtk_entry_set_text (GTK_ENTRY (entry), family);
5569     SPStyle *query =
5570         sp_style_new (SP_ACTIVE_DOCUMENT);
5572     int result_fontspec =
5573         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5575     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5577     SPCSSAttr *css = sp_repr_css_attr_new ();
5580     // First try to get the font spec from the stored value
5581     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5583     if (fontSpec.empty()) {
5584         // Construct a new font specification if it does not yet exist
5585         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5586         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5587         fontFromStyle->Unref();
5588     }
5590     if (!fontSpec.empty()) {
5591         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5592         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5593             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5594             if (font) {
5595                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5597                 // Set all the these just in case they were altered when finding the best
5598                 // match for the new family and old style...
5600                 gchar c[256];
5602                 font->Family(c, 256);
5603                 sp_repr_css_set_property (css, "font-family", c);
5605                 font->Attribute( "weight", c, 256);
5606                 sp_repr_css_set_property (css, "font-weight", c);
5608                 font->Attribute("style", c, 256);
5609                 sp_repr_css_set_property (css, "font-style", c);
5611                 font->Attribute("stretch", c, 256);
5612                 sp_repr_css_set_property (css, "font-stretch", c);
5614                 font->Attribute("variant", c, 256);
5615                 sp_repr_css_set_property (css, "font-variant", c);
5617                 font->Unref();
5618             }
5619         }
5620     }
5622     // If querying returned nothing, set the default style of the tool (for new texts)
5623     if (result_fontspec == QUERY_STYLE_NOTHING)
5624     {
5625         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5626         prefs->setStyle("/tools/text/style", css);
5627         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5628     }
5629     else
5630     {
5631         sp_desktop_set_style (desktop, css, true, true);
5632     }
5634     sp_style_unref(query);
5636     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5637                                    _("Text: Change font family"));
5638     sp_repr_css_attr_unref (css);
5639     g_free(family);
5640     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5642     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5645 /* This is where execution comes when the contents of the font family box have been completed
5646    by the press of the return key */
5647 void
5648 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5649                                        GObject      *tbl)
5651     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5653 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5654     try {
5655         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5656         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5657         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5658         gtk_tree_selection_select_path (selection, path.gobj());
5659         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5660         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5661     } catch (...) {
5662         if (family && strlen (family))
5663         {
5664             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5665         }
5666     }
5669 void
5670 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5671                                    gpointer          data)
5673     if (g_object_get_data (G_OBJECT (button), "block")) return;
5674     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5675     int prop = GPOINTER_TO_INT(data);
5677     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5678     SPCSSAttr *css = sp_repr_css_attr_new ();
5680     switch (prop)
5681     {
5682         case 0:
5683         {
5684             sp_repr_css_set_property (css, "text-anchor", "start");
5685             sp_repr_css_set_property (css, "text-align", "start");
5686             break;
5687         }
5688         case 1:
5689         {
5690             sp_repr_css_set_property (css, "text-anchor", "middle");
5691             sp_repr_css_set_property (css, "text-align", "center");
5692             break;
5693         }
5695         case 2:
5696         {
5697             sp_repr_css_set_property (css, "text-anchor", "end");
5698             sp_repr_css_set_property (css, "text-align", "end");
5699             break;
5700         }
5702         case 3:
5703         {
5704             sp_repr_css_set_property (css, "text-anchor", "start");
5705             sp_repr_css_set_property (css, "text-align", "justify");
5706             break;
5707         }
5708     }
5710     SPStyle *query =
5711         sp_style_new (SP_ACTIVE_DOCUMENT);
5712     int result_numbers =
5713         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5715     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5716     if (result_numbers == QUERY_STYLE_NOTHING)
5717     {
5718         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5719         prefs->setStyle("/tools/text/style", css);
5720     }
5722     sp_style_unref(query);
5724     sp_desktop_set_style (desktop, css, true, true);
5725     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5726                                    _("Text: Change alignment"));
5727     sp_repr_css_attr_unref (css);
5729     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5732 void
5733 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5734                                gpointer          data)
5736     if (g_object_get_data (G_OBJECT (button), "block")) return;
5738     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5739     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5740     int          prop       = GPOINTER_TO_INT(data);
5741     bool         active     = gtk_toggle_button_get_active (button);
5743     SPStyle *query =
5744         sp_style_new (SP_ACTIVE_DOCUMENT);
5746     int result_fontspec =
5747         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5749     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5750     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5751     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5753     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5754     Glib::ustring newFontSpec = "";
5756     if (fontSpec.empty()) {
5757         // Construct a new font specification if it does not yet exist
5758         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5759         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5760         fontFromStyle->Unref();
5761     }
5763     switch (prop)
5764     {
5765         case 0:
5766         {
5767             if (!fontSpec.empty()) {
5768                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5769             }
5770             if (fontSpec != newFontSpec) {
5771                 // Don't even set the bold if the font didn't exist on the system
5772                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5773             }
5774             break;
5775         }
5777         case 1:
5778         {
5779             if (!fontSpec.empty()) {
5780                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5781             }
5782             if (fontSpec != newFontSpec) {
5783                 // Don't even set the italic if the font didn't exist on the system
5784                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5785             }
5786             break;
5787         }
5788     }
5790     if (!newFontSpec.empty()) {
5791         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5792     }
5794     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5795     if (result_fontspec == QUERY_STYLE_NOTHING)
5796     {
5797         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5798         prefs->setStyle("/tools/text/style", css);
5799     }
5801     sp_style_unref(query);
5803     sp_desktop_set_style (desktop, css, true, true);
5804     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5805                                    _("Text: Change font style"));
5806     sp_repr_css_attr_unref (css);
5808     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5811 void
5812 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5813                                      gpointer         data)
5815     if (g_object_get_data (G_OBJECT (button), "block")) {
5816         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5817         return;
5818     }
5820     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5821     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5822     int          prop       = GPOINTER_TO_INT(data);
5824     switch (prop)
5825     {
5826         case 0:
5827         {
5828             sp_repr_css_set_property (css, "writing-mode", "lr");
5829             break;
5830         }
5832         case 1:
5833         {
5834             sp_repr_css_set_property (css, "writing-mode", "tb");
5835             break;
5836         }
5837     }
5839     SPStyle *query =
5840         sp_style_new (SP_ACTIVE_DOCUMENT);
5841     int result_numbers =
5842         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5844     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5845     if (result_numbers == QUERY_STYLE_NOTHING)
5846     {
5847         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5848         prefs->setStyle("/tools/text/style", css);
5849     }
5851     sp_desktop_set_style (desktop, css, true, true);
5852     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5853                                    _("Text: Change orientation"));
5854     sp_repr_css_attr_unref (css);
5856     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5859 gboolean
5860 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5862     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5863     if (!desktop) return FALSE;
5865     switch (get_group0_keyval (event)) {
5866         case GDK_Escape: // defocus
5867             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5868             sp_text_toolbox_selection_changed (NULL, tbl); // update
5869             return TRUE; // I consumed the event
5870             break;
5871     }
5872     return FALSE;
5875 gboolean
5876 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5878     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5879     if (!desktop) return FALSE;
5881     switch (get_group0_keyval (event)) {
5882         case GDK_KP_Enter:
5883         case GDK_Return:
5884         case GDK_Escape: // defocus
5885             gtk_widget_hide (w);
5886             popdown_visible = false;
5887             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5888             return TRUE; // I consumed the event
5889             break;
5890         case GDK_w:
5891         case GDK_W:
5892             if (event->state & GDK_CONTROL_MASK) {
5893                 gtk_widget_hide (w);
5894                 popdown_visible = false;
5895                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5896                 return TRUE; // I consumed the event
5897             }
5898             break;
5899     }
5900     return FALSE;
5904 void
5905 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5906                                GObject     *tbl)
5908     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5910     if (g_object_get_data (tbl, "size-block")) return;
5912     // If this is not from selecting a size in the list (in which case get_active will give the
5913     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5914     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5915     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5916     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5917         return;
5919     gdouble value = -1;
5920     {
5921         gchar *endptr;
5922         gchar *const text = gtk_combo_box_get_active_text(cbox);
5923         if (text) {
5924             value = g_strtod(text, &endptr);
5925             if (endptr == text) {  // Conversion failed, non-numeric input.
5926                 value = -1;
5927             }
5928             g_free(text);
5929         }
5930     }
5931     if (value <= 0) {
5932         return; // could not parse value
5933     }
5935     SPCSSAttr *css = sp_repr_css_attr_new ();
5936     Inkscape::CSSOStringStream osfs;
5937     osfs << value;
5938     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5940     SPStyle *query =
5941         sp_style_new (SP_ACTIVE_DOCUMENT);
5942     int result_numbers =
5943         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5945     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5946     if (result_numbers == QUERY_STYLE_NOTHING)
5947     {
5948         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5949         prefs->setStyle("/tools/text/style", css);
5950     }
5952     sp_style_unref(query);
5954     sp_desktop_set_style (desktop, css, true, true);
5955     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5956                                    _("Text: Change font size"));
5957     sp_repr_css_attr_unref (css);
5959     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5962 gboolean
5963 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5965     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5966     if (!desktop) return FALSE;
5968     if (!g_object_get_data (tbl, "esc-pressed")) {
5969         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5970         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5971         sp_text_toolbox_size_changed (cbox, tbl);
5972         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5973     }
5974     return FALSE; // I consumed the event
5978 gboolean
5979 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5981     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5982     if (!desktop) return FALSE;
5984     switch (get_group0_keyval (event)) {
5985         case GDK_Escape: // defocus
5986             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5987             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5988             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5989             return TRUE; // I consumed the event
5990             break;
5991         case GDK_Return: // defocus
5992         case GDK_KP_Enter:
5993             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5994             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5995             sp_text_toolbox_size_changed (cbox, tbl);
5996             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5997             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5998             return TRUE; // I consumed the event
5999             break;
6000     }
6001     return FALSE;
6004 void
6005 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
6006                                          GObject            *tbl)
6008     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6009     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6010     int x, y;
6012     if (!popdown_visible)
6013     {
6014         gdk_window_get_origin (widget->window, &x, &y);
6015         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6016         gtk_widget_show_all (popdown);
6017         //sp_transientize (popdown);
6019         gdk_pointer_grab (widget->window, TRUE,
6020                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6021                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6022                                         GDK_POINTER_MOTION_MASK),
6023                           NULL, NULL, GDK_CURRENT_TIME);
6025         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6027         popdown_visible = true;
6028     }
6029     else
6030     {
6031         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6032         gdk_pointer_ungrab (GDK_CURRENT_TIME);
6033         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6034         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6035         gtk_widget_hide (popdown);
6036         popdown_visible = false;
6037     }
6040 gboolean
6041 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6042                                  GdkEventFocus    */*event*/,
6043                                  GObject          */*tbl*/)
6045     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6046     return FALSE;
6049 gboolean
6050 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
6051                                    GdkEventFocus    */*event*/,
6052                                    GObject          */*tbl*/)
6054     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6056     if (popdown_hasfocus) {
6057         gtk_widget_hide (popdown);
6058         popdown_hasfocus = false;
6059         popdown_visible = false;
6060         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6061         return TRUE;
6062     }
6063     return FALSE;
6066 gboolean
6067 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
6068                                    GdkEventFocus    */*event*/,
6069                                    GObject          */*tbl*/)
6071     popdown_hasfocus = true;
6072     return TRUE;
6076 void
6077 cell_data_func  (GtkTreeViewColumn */*column*/,
6078                  GtkCellRenderer   *cell,
6079                  GtkTreeModel      *tree_model,
6080                  GtkTreeIter       *iter,
6081                  gpointer           /*data*/)
6083     gchar *family;
6084     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6085     gchar *const family_escaped = g_markup_escape_text(family, -1);
6087     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6088     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6090     std::stringstream markup;
6091     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6092            << family_escaped << "'>" << sample_escaped << "</span>";
6093     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6095     g_free(family);
6096     g_free(family_escaped);
6097     g_free(sample_escaped);
6100 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6101     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6102     if (completion) {
6103         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6104         g_object_unref (completion);
6105     }
6108 GtkWidget*
6109 sp_text_toolbox_new (SPDesktop *desktop)
6111     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6112     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6114     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6115     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6117     GtkTooltips *tt = gtk_tooltips_new();
6118     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6120     ////////////Family
6121     //Window
6122     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6123     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6125     //Entry
6126     GtkWidget           *entry = gtk_entry_new ();
6127     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6128     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6129     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6130     gtk_entry_completion_set_text_column (completion, 0);
6131     gtk_entry_completion_set_minimum_key_length (completion, 1);
6132     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6133     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6134     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6135     gtk_toolbar_append_widget( tbl, entry, "", "" );
6136     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6138     //Button
6139     GtkWidget   *button = gtk_button_new ();
6140     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6141     gtk_toolbar_append_widget( tbl, button, "", "");
6143     //Popdown
6144     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6145     GtkWidget           *treeview = gtk_tree_view_new ();
6147     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6148     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6149     gtk_tree_view_column_pack_start (column, cell, FALSE);
6150     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6151     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6152     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6154     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6155     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6156     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6158     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6160     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6161     gtk_container_add (GTK_CONTAINER (sw), treeview);
6163     gtk_container_add (GTK_CONTAINER (window), sw);
6164     gtk_widget_set_size_request (window, 300, 450);
6166     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6167     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6168     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6170     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6172     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6173     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6174     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6176     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6177     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6179     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6180     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6181     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6182     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6183     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6185     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6186     GtkWidget *box = gtk_event_box_new ();
6187     gtk_container_add (GTK_CONTAINER (box), image);
6188     gtk_toolbar_append_widget( tbl, box, "", "");
6189     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6190     GtkTooltips *tooltips = gtk_tooltips_new ();
6191     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6192     gtk_widget_hide (GTK_WIDGET (box));
6193     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6195     ////////////Size
6196     gchar const *const sizes[] = {
6197         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6198         "16", "18", "20", "22", "24", "28",
6199         "32", "36", "40", "48", "56", "64", "72", "144"
6200     };
6202     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6203     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6204         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6205     }
6206     gtk_widget_set_size_request (cbox, 80, -1);
6207     gtk_toolbar_append_widget( tbl, cbox, "", "");
6208     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6209     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6210     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6211     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6213     ////////////Text anchor
6214     GtkWidget *group   = gtk_radio_button_new (NULL);
6215     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6216     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6218     // left
6219     GtkWidget *rbutton = group;
6220     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6221     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6222     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6224     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6225     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6226     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6227     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6229     // center
6230     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6231     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6232     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6233     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6235     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6236     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6237     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6238     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6240     // right
6241     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6242     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6243     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6244     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6246     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6247     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6248     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6249     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6251     // fill
6252     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6253     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6254     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6255     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6257     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6258     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6259     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6260     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6262     gtk_toolbar_append_widget( tbl, row, "", "");
6264     //spacer
6265     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6267     ////////////Text style
6268     row = gtk_hbox_new (FALSE, 4);
6270     // bold
6271     rbutton = gtk_toggle_button_new ();
6272     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6273     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6274     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6275     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6277     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6278     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6279     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6281     // italic
6282     rbutton = gtk_toggle_button_new ();
6283     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6284     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6285     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6286     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6288     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6289     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6290     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6292     gtk_toolbar_append_widget( tbl, row, "", "");
6294     //spacer
6295     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6297     ////////////Text orientation
6298     group   = gtk_radio_button_new (NULL);
6299     row     = gtk_hbox_new (FALSE, 4);
6300     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6302     // horizontal
6303     rbutton = group;
6304     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6305     gtk_container_add           (GTK_CONTAINER (rbutton),
6306                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6307     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6308     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6310     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6311     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6312     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6314     // vertical
6315     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6316     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6317     gtk_container_add           (GTK_CONTAINER (rbutton),
6318                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6319     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6320     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6322     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6323     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6324     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6325     gtk_toolbar_append_widget( tbl, row, "", "" );
6328     //watch selection
6329     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6331     sigc::connection *c_selection_changed =
6332         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6333                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6334     pool->add_connection ("selection-changed", c_selection_changed);
6336     sigc::connection *c_selection_modified =
6337         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6338                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6339     pool->add_connection ("selection-modified", c_selection_modified);
6341     sigc::connection *c_subselection_changed =
6342         new sigc::connection (desktop->connectToolSubselectionChanged
6343                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6344     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6346     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6349     gtk_widget_show_all( GTK_WIDGET(tbl) );
6351     return GTK_WIDGET(tbl);
6352 } // end of sp_text_toolbox_new()
6354 }//<unnamed> namespace
6357 //#########################
6358 //##      Connector      ##
6359 //#########################
6361 static void sp_connector_path_set_avoid(void)
6363     cc_selection_set_avoid(true);
6367 static void sp_connector_path_set_ignore(void)
6369     cc_selection_set_avoid(false);
6374 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6376     // quit if run by the _changed callbacks
6377     if (g_object_get_data( tbl, "freeze" )) {
6378         return;
6379     }
6381     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6382     SPDocument *doc = sp_desktop_document(desktop);
6384     if (!sp_document_get_undo_sensitive(doc))
6385     {
6386         return;
6387     }
6389     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6391     if ( repr->attribute("inkscape:connector-spacing") ) {
6392         gdouble priorValue = gtk_adjustment_get_value(adj);
6393         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6394         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6395             return;
6396         }
6397     } else if ( adj->value == defaultConnSpacing ) {
6398         return;
6399     }
6401     // in turn, prevent callbacks from responding
6402     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6404     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6405     SP_OBJECT(desktop->namedview)->updateRepr();
6407     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6408     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6409         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6410         Geom::Matrix m = Geom::identity();
6411         avoid_item_move(&m, item);
6412     }
6414     if (items) {
6415         g_slist_free(items);
6416     }
6418     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6419             _("Change connector spacing"));
6421     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6423     spinbutton_defocus(GTK_OBJECT(tbl));
6426 static void sp_connector_graph_layout(void)
6428     if (!SP_ACTIVE_DESKTOP) return;
6429     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6431     // hack for clones, see comment in align-and-distribute.cpp
6432     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6433     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6435     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6437     prefs->setInt("/options/clonecompensation/value", saved_compensation);
6439     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6442 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6444     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6445     prefs->setBool("/tools/connector/directedlayout",
6446                 gtk_toggle_action_get_active( act ));
6449 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6451     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6452     prefs->setBool("/tools/connector/avoidoverlaplayout",
6453                 gtk_toggle_action_get_active( act ));
6457 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6459     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6460     prefs->setDouble("/tools/connector/length", adj->value);
6461     spinbutton_defocus(GTK_OBJECT(tbl));
6464 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6465                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6466                                             bool /*is_interactive*/, gpointer data)
6468     GtkWidget *tbl = GTK_WIDGET(data);
6470     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6471         return;
6472     }
6473     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6474         return;
6475     }
6477     GtkAdjustment *adj = (GtkAdjustment*)
6478             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6479     gdouble spacing = defaultConnSpacing;
6480     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6482     gtk_adjustment_set_value(adj, spacing);
6486 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6487     NULL, /* child_added */
6488     NULL, /* child_removed */
6489     connector_tb_event_attr_changed,
6490     NULL, /* content_changed */
6491     NULL  /* order_changed */
6492 };
6495 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6497     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6498     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6500     {
6501         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6502                                           _("Avoid"),
6503                                           _("Make connectors avoid selected objects"),
6504                                           "connector_avoid",
6505                                           secondarySize );
6506         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6507         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6508     }
6510     {
6511         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6512                                           _("Ignore"),
6513                                           _("Make connectors ignore selected objects"),
6514                                           "connector_ignore",
6515                                           secondarySize );
6516         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6517         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6518     }
6520     EgeAdjustmentAction* eact = 0;
6522     // Spacing spinbox
6523     eact = create_adjustment_action( "ConnectorSpacingAction",
6524                                      _("Connector Spacing"), _("Spacing:"),
6525                                      _("The amount of space left around objects by auto-routing connectors"),
6526                                      "/tools/connector/spacing", defaultConnSpacing,
6527                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6528                                      0, 100, 1.0, 10.0,
6529                                      0, 0, 0,
6530                                      connector_spacing_changed, 1, 0 );
6531     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6533     // Graph (connector network) layout
6534     {
6535         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6536                                           _("Graph"),
6537                                           _("Nicely arrange selected connector network"),
6538                                           "graph_layout",
6539                                           secondarySize );
6540         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6541         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6542     }
6544     // Default connector length spinbox
6545     eact = create_adjustment_action( "ConnectorLengthAction",
6546                                      _("Connector Length"), _("Length:"),
6547                                      _("Ideal length for connectors when layout is applied"),
6548                                      "/tools/connector/length", 100,
6549                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6550                                      10, 1000, 10.0, 100.0,
6551                                      0, 0, 0,
6552                                      connector_length_changed, 1, 0 );
6553     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6556     // Directed edges toggle button
6557     {
6558         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6559                                                       _("Downwards"),
6560                                                       _("Make connectors with end-markers (arrows) point downwards"),
6561                                                       "directed_graph",
6562                                                       Inkscape::ICON_SIZE_DECORATION );
6563         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6565         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
6566         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
6568         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6569     }
6571     // Avoid overlaps toggle button
6572     {
6573         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6574                                                       _("Remove overlaps"),
6575                                                       _("Do not allow overlapping shapes"),
6576                                                       "remove_overlaps",
6577                                                       Inkscape::ICON_SIZE_DECORATION );
6578         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6580         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
6581         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
6583         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6584     }
6586     // Code to watch for changes to the connector-spacing attribute in
6587     // the XML.
6588     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6589     g_assert(repr != NULL);
6591     purge_repr_listener( holder, holder );
6593     if (repr) {
6594         g_object_set_data( holder, "repr", repr );
6595         Inkscape::GC::anchor(repr);
6596         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6597         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6598     }
6599 } // end of sp_connector_toolbox_prep()
6602 //#########################
6603 //##     Paintbucket     ##
6604 //#########################
6606 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6608     gint channels = ege_select_one_action_get_active( act );
6609     flood_channels_set_channels( channels );
6612 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6614     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6615     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
6618 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6620     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6621     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
6624 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6626     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6627     SPUnit const *unit = tracker->getActiveUnit();
6628     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6630     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6631     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
6634 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6636     // FIXME: make defaults settable via Inkscape Options
6637     struct KeyValue {
6638         char const *key;
6639         double value;
6640     } const key_values[] = {
6641         {"threshold", 15},
6642         {"offset", 0.0}
6643     };
6645     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6646         KeyValue const &kv = key_values[i];
6647         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6648         if ( adj ) {
6649             gtk_adjustment_set_value(adj, kv.value);
6650         }
6651     }
6653     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6654     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6655     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6656     ege_select_one_action_set_active( autogap_action, 0 );
6659 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6661     EgeAdjustmentAction* eact = 0;
6662     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6664     {
6665         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6667         GList* items = 0;
6668         gint count = 0;
6669         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6670         {
6671             GtkTreeIter iter;
6672             gtk_list_store_append( model, &iter );
6673             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6674             count++;
6675         }
6676         g_list_free( items );
6677         items = 0;
6678         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6679         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6680         ege_select_one_action_set_appearance( act1, "compact" );
6681         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
6682         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6683         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6684         g_object_set_data( holder, "channels_action", act1 );
6685     }
6687     // Spacing spinbox
6688     {
6689         eact = create_adjustment_action(
6690             "ThresholdAction",
6691             _("Fill Threshold"), _("Threshold:"),
6692             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6693             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6694             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6695             0, 0, 0,
6696             paintbucket_threshold_changed, 1, 0 );
6698         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6699         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6700     }
6702     // Create the units menu.
6703     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6704     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
6705     if (!stored_unit.empty())
6706         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
6707     g_object_set_data( holder, "tracker", tracker );
6708     {
6709         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6710         gtk_action_group_add_action( mainActions, act );
6711     }
6713     // Offset spinbox
6714     {
6715         eact = create_adjustment_action(
6716             "OffsetAction",
6717             _("Grow/shrink by"), _("Grow/shrink by:"),
6718             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6719             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6720             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6721             0, 0, 0,
6722             paintbucket_offset_changed, 1, 2);
6723         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6725         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6726     }
6728     /* Auto Gap */
6729     {
6730         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6732         GList* items = 0;
6733         gint count = 0;
6734         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6735         {
6736             GtkTreeIter iter;
6737             gtk_list_store_append( model, &iter );
6738             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6739             count++;
6740         }
6741         g_list_free( items );
6742         items = 0;
6743         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6744         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6745         ege_select_one_action_set_appearance( act2, "compact" );
6746         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
6747         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6748         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6749         g_object_set_data( holder, "autogap_action", act2 );
6750     }
6752     /* Reset */
6753     {
6754         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6755                                           _("Defaults"),
6756                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6757                                           GTK_STOCK_CLEAR );
6758         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6759         gtk_action_group_add_action( mainActions, act );
6760         gtk_action_set_sensitive( act, TRUE );
6761     }
6765 /*
6766   Local Variables:
6767   mode:c++
6768   c-file-style:"stroustrup"
6769   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6770   indent-tabs-mode:nil
6771   fill-column:99
6772   End:
6773 */
6774 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :