Code

c2e80db784b3065c415ca4cbcbc26499db7032f1
[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>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "calligraphic-profile-rename.h"
40 #include "../conn-avoid-ref.h"
41 #include "../connection-pool.h"
42 #include "../connector-context.h"
43 #include "../desktop.h"
44 #include "../desktop-handles.h"
45 #include "../desktop-style.h"
46 #include "../dialogs/dialog-events.h"
47 #include "../dialogs/text-edit.h"
48 #include "../document-private.h"
49 #include "../ege-adjustment-action.h"
50 #include "../ege-output-action.h"
51 #include "../ege-select-one-action.h"
52 #include "../flood-context.h"
53 #include "gradient-toolbar.h"
54 #include "../graphlayout/graphlayout.h"
55 #include "../helper/unit-menu.h"
56 #include "../helper/units.h"
57 #include "../helper/unit-tracker.h"
58 #include "icon.h"
59 #include "../ink-action.h"
60 #include "../inkscape.h"
61 #include "../inkscape-stock.h"
62 #include "../interface.h"
63 #include "../libnrtype/font-instance.h"
64 #include "../libnrtype/font-lister.h"
65 #include "../live_effects/effect.h"
66 #include "../live_effects/lpe-angle_bisector.h"
67 #include "../live_effects/lpe-line_segment.h"
68 #include "../lpe-tool-context.h"
69 #include "../mod360.h"
70 #include "../node-context.h"
71 #include "../pen-context.h"
72 #include "../preferences.h"
73 #include "../selection-chemistry.h"
74 #include "../selection.h"
75 #include "select-toolbar.h"
76 #include "../shape-editor.h"
77 #include "../shortcuts.h"
78 #include "../sp-clippath.h"
79 #include "../sp-ellipse.h"
80 #include "../sp-flowtext.h"
81 #include "../sp-mask.h"
82 #include "../sp-namedview.h"
83 #include "../sp-rect.h"
84 #include "../sp-spiral.h"
85 #include "../sp-star.h"
86 #include "../sp-text.h"
87 #include "../style.h"
88 #include "../svg/css-ostringstream.h"
89 #include "../tools-switch.h"
90 #include "../tweak-context.h"
91 #include "../ui/widget/style-swatch.h"
92 #include "../verbs.h"
93 #include "../widgets/button.h"
94 #include "../widgets/spinbutton-events.h"
95 #include "../widgets/spw-utilities.h"
96 #include "../widgets/widget-sizes.h"
97 #include "../xml/attribute-record.h"
98 #include "../xml/node-event-vector.h"
99 #include "../xml/repr.h"
101 #include "toolbox.h"
103 using Inkscape::UnitTracker;
105 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
106 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
108 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
121 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
129 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
130     static Inkscape::IconSize sizeChoices[] = {
131         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
132         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
133         Inkscape::ICON_SIZE_MENU
134     };
135     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
136     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
137     return sizeChoices[index];
140 static struct {
141     gchar const *type_name;
142     gchar const *data_name;
143     sp_verb_t verb;
144     sp_verb_t doubleclick_verb;
145 } const tools[] = {
146     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
147     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
148     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
149     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
150     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
151     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
152     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
153     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
154     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
155     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
156     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
157     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
158     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
159     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
160     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
161     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
162     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
163     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
164     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
165     { NULL, NULL, 0, 0 }
166 };
168 static struct {
169     gchar const *type_name;
170     gchar const *data_name;
171     GtkWidget *(*create_func)(SPDesktop *desktop);
172     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
173     gchar const *ui_name;
174     gint swatch_verb_id;
175     gchar const *swatch_tool;
176     gchar const *swatch_tip;
177 } const aux_toolboxes[] = {
178     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
179       SP_VERB_INVALID, 0, 0},
180     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
181       SP_VERB_INVALID, 0, 0},
182     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
183       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
184     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
187       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
188     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
189       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
190     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
191       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
192     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
193       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
194     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
195       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
196     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
197       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
198     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
199       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
200     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
201       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
202     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
203       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
204     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
205       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
206     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
207       SP_VERB_INVALID, 0, 0},
208     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
209       SP_VERB_INVALID, 0, 0},
210     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
211       SP_VERB_INVALID, 0, 0},
212     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
213       SP_VERB_INVALID, 0, 0},
214     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
215       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
216     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
217 };
219 #define TOOLBAR_SLIDER_HINT "full"
221 static gchar const * ui_descr =
222         "<ui>"
223         "  <toolbar name='SelectToolbar'>"
224         "    <toolitem action='EditSelectAll' />"
225         "    <toolitem action='EditSelectAllInAllLayers' />"
226         "    <toolitem action='EditDeselect' />"
227         "    <separator />"
228         "    <toolitem action='ObjectRotate90CCW' />"
229         "    <toolitem action='ObjectRotate90' />"
230         "    <toolitem action='ObjectFlipHorizontally' />"
231         "    <toolitem action='ObjectFlipVertically' />"
232         "    <separator />"
233         "    <toolitem action='SelectionToBack' />"
234         "    <toolitem action='SelectionLower' />"
235         "    <toolitem action='SelectionRaise' />"
236         "    <toolitem action='SelectionToFront' />"
237         "    <separator />"
238         "    <toolitem action='XAction' />"
239         "    <toolitem action='YAction' />"
240         "    <toolitem action='WidthAction' />"
241         "    <toolitem action='LockAction' />"
242         "    <toolitem action='HeightAction' />"
243         "    <toolitem action='UnitsAction' />"
244         "    <separator />"
245         "    <toolitem action='transform_affect_label' />"
246         "    <toolitem action='transform_stroke' />"
247         "    <toolitem action='transform_corners' />"
248         "    <toolitem action='transform_gradient' />"
249         "    <toolitem action='transform_pattern' />"
250         "  </toolbar>"
252         "  <toolbar name='NodeToolbar'>"
253         "    <toolitem action='NodeInsertAction' />"
254         "    <toolitem action='NodeDeleteAction' />"
255         "    <separator />"
256         "    <toolitem action='NodeJoinAction' />"
257         "    <toolitem action='NodeBreakAction' />"
258         "    <separator />"
259         "    <toolitem action='NodeJoinSegmentAction' />"
260         "    <toolitem action='NodeDeleteSegmentAction' />"
261         "    <separator />"
262         "    <toolitem action='NodeCuspAction' />"
263         "    <toolitem action='NodeSmoothAction' />"
264         "    <toolitem action='NodeSymmetricAction' />"
265         "    <toolitem action='NodeAutoAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeLineAction' />"
268         "    <toolitem action='NodeCurveAction' />"
269         "    <separator />"
270         "    <toolitem action='ObjectToPath' />"
271         "    <toolitem action='StrokeToPath' />"
272         "    <separator />"
273         "    <toolitem action='NodeXAction' />"
274         "    <toolitem action='NodeYAction' />"
275         "    <toolitem action='NodeUnitsAction' />"
276         "    <separator />"
277         "    <toolitem action='ObjectEditClipPathAction' />"
278         "    <toolitem action='ObjectEditMaskPathAction' />"
279         "    <toolitem action='EditNextLPEParameterAction' />"
280         "    <separator />"
281         "    <toolitem action='NodesShowHandlesAction' />"
282         "    <toolitem action='NodesShowHelperpath' />"
283         "  </toolbar>"
285         "  <toolbar name='TweakToolbar'>"
286         "    <toolitem action='TweakWidthAction' />"
287         "    <separator />"
288         "    <toolitem action='TweakForceAction' />"
289         "    <toolitem action='TweakPressureAction' />"
290         "    <separator />"
291         "    <toolitem action='TweakModeAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakFidelityAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakChannelsLabel' />"
296         "    <toolitem action='TweakDoH' />"
297         "    <toolitem action='TweakDoS' />"
298         "    <toolitem action='TweakDoL' />"
299         "    <toolitem action='TweakDoO' />"
300         "  </toolbar>"
302         "  <toolbar name='ZoomToolbar'>"
303         "    <toolitem action='ZoomIn' />"
304         "    <toolitem action='ZoomOut' />"
305         "    <separator />"
306         "    <toolitem action='Zoom1:0' />"
307         "    <toolitem action='Zoom1:2' />"
308         "    <toolitem action='Zoom2:1' />"
309         "    <separator />"
310         "    <toolitem action='ZoomSelection' />"
311         "    <toolitem action='ZoomDrawing' />"
312         "    <toolitem action='ZoomPage' />"
313         "    <toolitem action='ZoomPageWidth' />"
314         "    <separator />"
315         "    <toolitem action='ZoomPrev' />"
316         "    <toolitem action='ZoomNext' />"
317         "  </toolbar>"
319         "  <toolbar name='StarToolbar'>"
320         "    <separator />"
321         "    <toolitem action='StarStateAction' />"
322         "    <separator />"
323         "    <toolitem action='FlatAction' />"
324         "    <separator />"
325         "    <toolitem action='MagnitudeAction' />"
326         "    <toolitem action='SpokeAction' />"
327         "    <toolitem action='RoundednessAction' />"
328         "    <toolitem action='RandomizationAction' />"
329         "    <separator />"
330         "    <toolitem action='StarResetAction' />"
331         "  </toolbar>"
333         "  <toolbar name='RectToolbar'>"
334         "    <toolitem action='RectStateAction' />"
335         "    <toolitem action='RectWidthAction' />"
336         "    <toolitem action='RectHeightAction' />"
337         "    <toolitem action='RadiusXAction' />"
338         "    <toolitem action='RadiusYAction' />"
339         "    <toolitem action='RectUnitsAction' />"
340         "    <separator />"
341         "    <toolitem action='RectResetAction' />"
342         "  </toolbar>"
344         "  <toolbar name='3DBoxToolbar'>"
345         "    <toolitem action='3DBoxAngleXAction' />"
346         "    <toolitem action='3DBoxVPXStateAction' />"
347         "    <separator />"
348         "    <toolitem action='3DBoxAngleYAction' />"
349         "    <toolitem action='3DBoxVPYStateAction' />"
350         "    <separator />"
351         "    <toolitem action='3DBoxAngleZAction' />"
352         "    <toolitem action='3DBoxVPZStateAction' />"
353         "  </toolbar>"
355         "  <toolbar name='SpiralToolbar'>"
356         "    <toolitem action='SpiralStateAction' />"
357         "    <toolitem action='SpiralRevolutionAction' />"
358         "    <toolitem action='SpiralExpansionAction' />"
359         "    <toolitem action='SpiralT0Action' />"
360         "    <separator />"
361         "    <toolitem action='SpiralResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='PenToolbar'>"
365         "    <toolitem action='FreehandModeActionPen' />"
366         "    <separator />"
367         "    <toolitem action='SetPenShapeAction'/>"
368         "  </toolbar>"
370         "  <toolbar name='PencilToolbar'>"
371         "    <toolitem action='FreehandModeActionPencil' />"
372         "    <separator />"
373         "    <toolitem action='PencilToleranceAction' />"
374         "    <separator />"
375         "    <toolitem action='PencilResetAction' />"
376         "    <separator />"
377         "    <toolitem action='SetPencilShapeAction'/>"
378         "  </toolbar>"
380         "  <toolbar name='CalligraphyToolbar'>"
381         "    <separator />"
382         "    <toolitem action='SetProfileAction'/>"
383         "    <separator />"
384         "    <toolitem action='CalligraphyWidthAction' />"
385         "    <toolitem action='PressureAction' />"
386         "    <toolitem action='TraceAction' />"
387         "    <toolitem action='ThinningAction' />"
388         "    <separator />"
389         "    <toolitem action='AngleAction' />"
390         "    <toolitem action='TiltAction' />"
391         "    <toolitem action='FixationAction' />"
392         "    <separator />"
393         "    <toolitem action='CapRoundingAction' />"
394         "    <separator />"
395         "    <toolitem action='TremorAction' />"
396         "    <toolitem action='WiggleAction' />"
397         "    <toolitem action='MassAction' />"
398         "    <separator />"
399         "  </toolbar>"
401         "  <toolbar name='ArcToolbar'>"
402         "    <toolitem action='ArcStateAction' />"
403         "    <separator />"
404         "    <toolitem action='ArcStartAction' />"
405         "    <toolitem action='ArcEndAction' />"
406         "    <separator />"
407         "    <toolitem action='ArcOpenAction' />"
408         "    <separator />"
409         "    <toolitem action='ArcResetAction' />"
410         "    <separator />"
411         "  </toolbar>"
413         "  <toolbar name='PaintbucketToolbar'>"
414         "    <toolitem action='ChannelsAction' />"
415         "    <separator />"
416         "    <toolitem action='ThresholdAction' />"
417         "    <separator />"
418         "    <toolitem action='OffsetAction' />"
419         "    <toolitem action='PaintbucketUnitsAction' />"
420         "    <separator />"
421         "    <toolitem action='AutoGapAction' />"
422         "    <separator />"
423         "    <toolitem action='PaintbucketResetAction' />"
424         "  </toolbar>"
426         "  <toolbar name='EraserToolbar'>"
427         "    <toolitem action='EraserWidthAction' />"
428         "    <separator />"
429         "    <toolitem action='EraserModeAction' />"
430         "  </toolbar>"
432         "  <toolbar name='LPEToolToolbar'>"
433         "    <toolitem action='LPEToolModeAction' />"
434         "    <separator />"
435         "    <toolitem action='LPEShowBBoxAction' />"
436         "    <toolitem action='LPEBBoxFromSelectionAction' />"
437         "    <separator />"
438         "    <toolitem action='LPELineSegmentAction' />"
439         "    <separator />"
440         "    <toolitem action='LPEMeasuringAction' />"
441         "    <toolitem action='LPEToolUnitsAction' />"
442         "    <separator />"
443         "    <toolitem action='LPEOpenLPEDialogAction' />"
444         "  </toolbar>"
446         "  <toolbar name='DropperToolbar'>"
447         "    <toolitem action='DropperOpacityAction' />"
448         "    <toolitem action='DropperPickAlphaAction' />"
449         "    <toolitem action='DropperSetAlphaAction' />"
450         "  </toolbar>"
452         "  <toolbar name='ConnectorToolbar'>"
453         "    <toolitem action='ConnectorAvoidAction' />"
454         "    <toolitem action='ConnectorIgnoreAction' />"
455         "    <toolitem action='ConnectorSpacingAction' />"
456         "    <toolitem action='ConnectorGraphAction' />"
457         "    <toolitem action='ConnectorLengthAction' />"
458         "    <toolitem action='ConnectorDirectedAction' />"
459         "    <toolitem action='ConnectorOverlapAction' />"
460         "  </toolbar>"
462         "</ui>"
465 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
467 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
469 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
470 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
472 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
473 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
475 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
476 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
479 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
480                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
481                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
483 class VerbAction : public Gtk::Action {
484 public:
485     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
487     virtual ~VerbAction();
488     virtual void set_active(bool active = true);
490 protected:
491     virtual Gtk::Widget* create_menu_item_vfunc();
492     virtual Gtk::Widget* create_tool_item_vfunc();
494     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
495     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
497     virtual void on_activate();
499 private:
500     Inkscape::Verb* verb;
501     Inkscape::Verb* verb2;
502     Inkscape::UI::View::View *view;
503     GtkTooltips *tooltips;
504     bool active;
506     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
507 };
510 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
512     Glib::RefPtr<VerbAction> result;
513     SPAction *action = verb->get_action(view);
514     if ( action ) {
515         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
516         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
517     }
519     return result;
522 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
523     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
524     verb(verb),
525     verb2(verb2),
526     view(view),
527     tooltips(tooltips),
528     active(false)
532 VerbAction::~VerbAction()
536 Gtk::Widget* VerbAction::create_menu_item_vfunc()
538 // First call in to get the icon rendered if present in SVG
539     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
540     delete widget;
541     widget = 0;
543     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
544 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
545     return widg;
548 Gtk::Widget* VerbAction::create_tool_item_vfunc()
550 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
551     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
552     GtkWidget* toolbox = 0;
553     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
554                                                                           SP_BUTTON_TYPE_TOGGLE,
555                                                                           verb,
556                                                                           verb2,
557                                                                           view,
558                                                                           tooltips );
559     if ( active ) {
560         sp_button_toggle_set_down( SP_BUTTON(button), active);
561     }
562     gtk_widget_show_all( button );
563     Gtk::Widget* wrapped = Glib::wrap(button);
564     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
565     holder->add(*wrapped);
567 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
568     return holder;
571 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
573 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
574     Gtk::Action::connect_proxy_vfunc(proxy);
577 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
579 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
580     Gtk::Action::disconnect_proxy_vfunc(proxy);
583 void VerbAction::set_active(bool active)
585     this->active = active;
586     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
587     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
588         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
589         if (ti) {
590             // *should* have one child that is the SPButton
591             Gtk::Widget* child = ti->get_child();
592             if ( child && SP_IS_BUTTON(child->gobj()) ) {
593                 SPButton* button = SP_BUTTON(child->gobj());
594                 sp_button_toggle_set_down( button, active );
595             }
596         }
597     }
600 void VerbAction::on_activate()
602     if ( verb ) {
603         SPAction *action = verb->get_action(view);
604         if ( action ) {
605             sp_action_perform(action, 0);
606         }
607     }
610 /* Global text entry widgets necessary for update */
611 /* GtkWidget *dropper_rgb_entry,
612           *dropper_opacity_entry ; */
613 // should be made a private member once this is converted to class
615 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
616     connection->disconnect();
617     delete connection;
620 static void purge_repr_listener( GObject* obj, GObject* tbl )
622     (void)obj;
623     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
624     if (oldrepr) { // remove old listener
625         sp_repr_remove_listener_by_data(oldrepr, tbl);
626         Inkscape::GC::release(oldrepr);
627         oldrepr = 0;
628         g_object_set_data( tbl, "repr", NULL );
629     }
632 GtkWidget *
633 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
634                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
635                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
637     SPAction *action = verb->get_action(view);
638     if (!action) return NULL;
640     SPAction *doubleclick_action;
641     if (doubleclick_verb)
642         doubleclick_action = doubleclick_verb->get_action(view);
643     else
644         doubleclick_action = NULL;
646     /* fixme: Handle sensitive/unsensitive */
647     /* fixme: Implement sp_button_new_from_action */
648     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
649     gtk_widget_show(b);
652     unsigned int shortcut = sp_shortcut_get_primary(verb);
653     if (shortcut) {
654         gchar key[256];
655         sp_ui_shortcut_string(shortcut, key);
656         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
657         if ( t ) {
658             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
659         }
660         g_free(tip);
661     } else {
662         if ( t ) {
663             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
664         }
665     }
667     return b;
671 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
673     SPAction* targetAction = SP_ACTION(user_data);
674     if ( targetAction ) {
675         sp_action_perform( targetAction, NULL );
676     }
679 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
681     if ( data ) {
682         GtkAction* act = GTK_ACTION(data);
683         gtk_action_set_sensitive( act, sensitive );
684     }
687 static SPActionEventVector action_event_vector = {
688     {NULL},
689     NULL,
690     NULL,
691     sp_action_action_set_sensitive,
692     NULL,
693     NULL
694 };
696 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
698     GtkAction* act = 0;
700     SPAction* targetAction = verb->get_action(view);
701     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
702     act = GTK_ACTION(inky);
703     gtk_action_set_sensitive( act, targetAction->sensitive );
705     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
707     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
708     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
710     return act;
713 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
715     Inkscape::UI::View::View *view = desktop;
716     gint verbsToUse[] = {
717         // disabled until we have icons for them:
718         //find
719         //SP_VERB_EDIT_TILE,
720         //SP_VERB_EDIT_UNTILE,
721         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
722         SP_VERB_DIALOG_DISPLAY,
723         SP_VERB_DIALOG_FILL_STROKE,
724         SP_VERB_DIALOG_NAMEDVIEW,
725         SP_VERB_DIALOG_TEXT,
726         SP_VERB_DIALOG_XML_EDITOR,
727         SP_VERB_EDIT_CLONE,
728         SP_VERB_EDIT_COPY,
729         SP_VERB_EDIT_CUT,
730         SP_VERB_EDIT_DUPLICATE,
731         SP_VERB_EDIT_PASTE,
732         SP_VERB_EDIT_REDO,
733         SP_VERB_EDIT_UNDO,
734         SP_VERB_EDIT_UNLINK_CLONE,
735         SP_VERB_FILE_EXPORT,
736         SP_VERB_FILE_IMPORT,
737         SP_VERB_FILE_NEW,
738         SP_VERB_FILE_OPEN,
739         SP_VERB_FILE_PRINT,
740         SP_VERB_FILE_SAVE,
741         SP_VERB_OBJECT_TO_CURVE,
742         SP_VERB_SELECTION_GROUP,
743         SP_VERB_SELECTION_OUTLINE,
744         SP_VERB_SELECTION_UNGROUP,
745         SP_VERB_ZOOM_1_1,
746         SP_VERB_ZOOM_1_2,
747         SP_VERB_ZOOM_2_1,
748         SP_VERB_ZOOM_DRAWING,
749         SP_VERB_ZOOM_IN,
750         SP_VERB_ZOOM_NEXT,
751         SP_VERB_ZOOM_OUT,
752         SP_VERB_ZOOM_PAGE,
753         SP_VERB_ZOOM_PAGE_WIDTH,
754         SP_VERB_ZOOM_PREV,
755         SP_VERB_ZOOM_SELECTION,
756     };
758     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
760     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
761     Glib::RefPtr<Gtk::ActionGroup> mainActions;
762     if ( groups.find(desktop) != groups.end() ) {
763         mainActions = groups[desktop];
764     }
766     if ( !mainActions ) {
767         mainActions = Gtk::ActionGroup::create("main");
768         groups[desktop] = mainActions;
769     }
771     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
772         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
773         if ( verb ) {
774             if (!mainActions->get_action(verb->get_id())) {
775                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
776                 mainActions->add(Glib::wrap(act));
777             }
778         }
779     }
781     if ( !mainActions->get_action("ToolZoom") ) {
782         GtkTooltips *tt = gtk_tooltips_new();
783         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
784             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
785             if ( va ) {
786                 mainActions->add(va);
787                 if ( i == 0 ) {
788                     va->set_active(true);
789                 }
790             }
791         }
792     }
795     return mainActions;
799 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
801     gtk_widget_set_size_request( widget,
802                                  widget->allocation.width,
803                                  widget->allocation.height );
806 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
808     gtk_widget_set_size_request( widget, -1, -1 );
813 GtkWidget *
814 sp_tool_toolbox_new()
816     GtkTooltips *tt = gtk_tooltips_new();
817     GtkWidget* tb = gtk_toolbar_new();
818     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
819     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
821     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
822     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
824     gtk_widget_set_sensitive(tb, FALSE);
826     GtkWidget *hb = gtk_handle_box_new();
827     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
828     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
829     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
831     gtk_container_add(GTK_CONTAINER(hb), tb);
832     gtk_widget_show(GTK_WIDGET(tb));
834     sigc::connection* conn = new sigc::connection;
835     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
837     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
838     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
840     return hb;
843 GtkWidget *
844 sp_aux_toolbox_new()
846     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
848     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
850     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
852     gtk_widget_set_sensitive(tb, FALSE);
854     GtkWidget *hb = gtk_handle_box_new();
855     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
856     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
857     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
859     gtk_container_add(GTK_CONTAINER(hb), tb);
860     gtk_widget_show(GTK_WIDGET(tb));
862     sigc::connection* conn = new sigc::connection;
863     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
865     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
866     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
868     return hb;
871 //####################################
872 //# Commands Bar
873 //####################################
875 GtkWidget *
876 sp_commands_toolbox_new()
878     GtkWidget *tb = gtk_toolbar_new();
880     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
881     gtk_widget_set_sensitive(tb, FALSE);
883     GtkWidget *hb = gtk_handle_box_new();
884     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
885     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
886     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
888     gtk_container_add(GTK_CONTAINER(hb), tb);
889     gtk_widget_show(GTK_WIDGET(tb));
891     sigc::connection* conn = new sigc::connection;
892     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
894     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
895     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
897     return hb;
901 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
902                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
903                                                        Glib::ustring const &path, gdouble def,
904                                                        GtkWidget *focusTarget,
905                                                        GtkWidget *us,
906                                                        GObject *dataKludge,
907                                                        gboolean altx, gchar const *altx_mark,
908                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
909                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
910                                                        void (*callback)(GtkAdjustment *, GObject *),
911                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
913     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
914     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
915                                                              lower, upper, step, page, page ) );
916     if (us) {
917         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
918     }
920     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
922     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
923     if ( shortLabel ) {
924         g_object_set( act, "short_label", shortLabel, NULL );
925     }
927     if ( (descrCount > 0) && descrLabels && descrValues ) {
928         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
929     }
931     if ( focusTarget ) {
932         ege_adjustment_action_set_focuswidget( act, focusTarget );
933     }
935     if ( altx && altx_mark ) {
936         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
937     }
939     if ( dataKludge ) {
940         // Rather lame, but it's the only place where we need to get the entry name
941         // but we don't have an Entry
942         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
943     }
945     // Using a cast just to make sure we pass in the right kind of function pointer
946     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
948     return act;
952 //####################################
953 //# node editing callbacks
954 //####################################
956 /**
957  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
958  */
959 static ShapeEditor *get_current_shape_editor()
961     if (!SP_ACTIVE_DESKTOP) {
962         return NULL;
963     }
965     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
967     if (!SP_IS_NODE_CONTEXT(event_context)) {
968         return NULL;
969     }
971     return event_context->shape_editor;
975 void
976 sp_node_path_edit_add(void)
978     ShapeEditor *shape_editor = get_current_shape_editor();
979     if (shape_editor) shape_editor->add_node();
982 void
983 sp_node_path_edit_delete(void)
985     ShapeEditor *shape_editor = get_current_shape_editor();
986     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
989 void
990 sp_node_path_edit_delete_segment(void)
992     ShapeEditor *shape_editor = get_current_shape_editor();
993     if (shape_editor) shape_editor->delete_segment();
996 void
997 sp_node_path_edit_break(void)
999     ShapeEditor *shape_editor = get_current_shape_editor();
1000     if (shape_editor) shape_editor->break_at_nodes();
1003 void
1004 sp_node_path_edit_join(void)
1006     ShapeEditor *shape_editor = get_current_shape_editor();
1007     if (shape_editor) shape_editor->join_nodes();
1010 void
1011 sp_node_path_edit_join_segment(void)
1013     ShapeEditor *shape_editor = get_current_shape_editor();
1014     if (shape_editor) shape_editor->join_segments();
1017 void
1018 sp_node_path_edit_toline(void)
1020     ShapeEditor *shape_editor = get_current_shape_editor();
1021     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1024 void
1025 sp_node_path_edit_tocurve(void)
1027     ShapeEditor *shape_editor = get_current_shape_editor();
1028     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1031 void
1032 sp_node_path_edit_cusp(void)
1034     ShapeEditor *shape_editor = get_current_shape_editor();
1035     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1038 void
1039 sp_node_path_edit_smooth(void)
1041     ShapeEditor *shape_editor = get_current_shape_editor();
1042     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1045 void
1046 sp_node_path_edit_symmetrical(void)
1048     ShapeEditor *shape_editor = get_current_shape_editor();
1049     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1052 void
1053 sp_node_path_edit_auto(void)
1055     ShapeEditor *shape_editor = get_current_shape_editor();
1056     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1059 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1060     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1061     bool show = gtk_toggle_action_get_active( act );
1062     prefs->setBool("/tools/nodes/show_handles",  show);
1063     ShapeEditor *shape_editor = get_current_shape_editor();
1064     if (shape_editor) shape_editor->show_handles(show);
1067 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1068     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1069     bool show = gtk_toggle_action_get_active( act );
1070     prefs->setBool("/tools/nodes/show_helperpath",  show);
1071     ShapeEditor *shape_editor = get_current_shape_editor();
1072     if (shape_editor) shape_editor->show_helperpath(show);
1075 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1076     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1079 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1080     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1083 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1084     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1087 /* is called when the node selection is modified */
1088 static void
1089 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1091     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1092     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1093     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1094     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1096     // quit if run by the attr_changed listener
1097     if (g_object_get_data( tbl, "freeze" )) {
1098         return;
1099     }
1101     // in turn, prevent listener from responding
1102     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1104     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1105     SPUnit const *unit = tracker->getActiveUnit();
1107     ShapeEditor *shape_editor = get_current_shape_editor();
1108     if (shape_editor && shape_editor->has_nodepath()) {
1109         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1110         int n_selected = 0;
1111         if (nodepath) {
1112             n_selected = nodepath->numSelected();
1113         }
1115         if (n_selected == 0) {
1116             gtk_action_set_sensitive(xact, FALSE);
1117             gtk_action_set_sensitive(yact, FALSE);
1118         } else {
1119             gtk_action_set_sensitive(xact, TRUE);
1120             gtk_action_set_sensitive(yact, TRUE);
1121             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1122             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1124             if (n_selected == 1) {
1125                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1126                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1127                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1128                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1129                 }
1130             } else {
1131                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1132                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1133                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1134                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1135                        selected nodes don't coincide (in this case we use the coordinates of the center
1136                        of the bounding box). So the entries are never set to zero. */
1137                     // FIXME: Maybe we should clear the entry if several nodes are selected
1138                     //        instead of providing a kind of average value
1139                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1140                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1141                 }
1142             }
1143         }
1144     } else {
1145         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1146         gtk_action_set_sensitive(xact, FALSE);
1147         gtk_action_set_sensitive(yact, FALSE);
1148     }
1150     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1153 static void
1154 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1156     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1157     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1159     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1160     SPUnit const *unit = tracker->getActiveUnit();
1162     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1163         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1164     }
1166     // quit if run by the attr_changed listener
1167     if (g_object_get_data( tbl, "freeze" )) {
1168         return;
1169     }
1171     // in turn, prevent listener from responding
1172     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1174     ShapeEditor *shape_editor = get_current_shape_editor();
1175     if (shape_editor && shape_editor->has_nodepath()) {
1176         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1177         if (!strcmp(value_name, "x")) {
1178             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1179         }
1180         if (!strcmp(value_name, "y")) {
1181             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1182         }
1183     }
1185     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1188 static void
1189 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1191     sp_node_path_value_changed(adj, tbl, "x");
1194 static void
1195 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1197     sp_node_path_value_changed(adj, tbl, "y");
1200 void
1201 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1203     {
1204     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1205     SPItem *item = selection->singleItem();
1206     if (item && SP_IS_LPE_ITEM(item)) {
1207        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1208            gtk_action_set_sensitive(w, TRUE);
1209        } else {
1210            gtk_action_set_sensitive(w, FALSE);
1211        }
1212     } else {
1213        gtk_action_set_sensitive(w, FALSE);
1214     }
1215     }
1217     {
1218     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1219     SPItem *item = selection->singleItem();
1220     if (item && item->clip_ref && item->clip_ref->getObject()) {
1221        gtk_action_set_sensitive(w, TRUE);
1222     } else {
1223        gtk_action_set_sensitive(w, FALSE);
1224     }
1225     }
1227     {
1228     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1229     SPItem *item = selection->singleItem();
1230     if (item && item->mask_ref && item->mask_ref->getObject()) {
1231        gtk_action_set_sensitive(w, TRUE);
1232     } else {
1233        gtk_action_set_sensitive(w, FALSE);
1234     }
1235     }
1238 void
1239 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1241     sp_node_toolbox_sel_changed (selection, tbl);
1246 //################################
1247 //##    Node Editing Toolbox    ##
1248 //################################
1250 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1252     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1253     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1254     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1255     g_object_set_data( holder, "tracker", tracker );
1257     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1259     {
1260         InkAction* inky = ink_action_new( "NodeInsertAction",
1261                                           _("Insert node"),
1262                                           _("Insert new nodes into selected segments"),
1263                                           "node_insert",
1264                                           secondarySize );
1265         g_object_set( inky, "short_label", _("Insert"), NULL );
1266         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1267         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1268     }
1270     {
1271         InkAction* inky = ink_action_new( "NodeDeleteAction",
1272                                           _("Delete node"),
1273                                           _("Delete selected nodes"),
1274                                           "node_delete",
1275                                           secondarySize );
1276         g_object_set( inky, "short_label", _("Delete"), NULL );
1277         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1278         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1279     }
1281     {
1282         InkAction* inky = ink_action_new( "NodeJoinAction",
1283                                           _("Join endnodes"),
1284                                           _("Join selected endnodes"),
1285                                           "node_join",
1286                                           secondarySize );
1287         g_object_set( inky, "short_label", _("Join"), NULL );
1288         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1289         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1290     }
1292     {
1293         InkAction* inky = ink_action_new( "NodeBreakAction",
1294                                           _("Break nodes"),
1295                                           _("Break path at selected nodes"),
1296                                           "node_break",
1297                                           secondarySize );
1298         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1299         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1300     }
1303     {
1304         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1305                                           _("Join with segment"),
1306                                           _("Join selected endnodes with a new segment"),
1307                                           "node_join_segment",
1308                                           secondarySize );
1309         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1310         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1311     }
1313     {
1314         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1315                                           _("Delete segment"),
1316                                           _("Delete segment between two non-endpoint nodes"),
1317                                           "node_delete_segment",
1318                                           secondarySize );
1319         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1320         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1321     }
1323     {
1324         InkAction* inky = ink_action_new( "NodeCuspAction",
1325                                           _("Node Cusp"),
1326                                           _("Make selected nodes corner"),
1327                                           "node_cusp",
1328                                           secondarySize );
1329         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1330         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1331     }
1333     {
1334         InkAction* inky = ink_action_new( "NodeSmoothAction",
1335                                           _("Node Smooth"),
1336                                           _("Make selected nodes smooth"),
1337                                           "node_smooth",
1338                                           secondarySize );
1339         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1340         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1341     }
1343     {
1344         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1345                                           _("Node Symmetric"),
1346                                           _("Make selected nodes symmetric"),
1347                                           "node_symmetric",
1348                                           secondarySize );
1349         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1350         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1351     }
1353     {
1354         InkAction* inky = ink_action_new( "NodeAutoAction",
1355                                           _("Node Auto"),
1356                                           _("Make selected nodes auto-smooth"),
1357                                           "node_auto",
1358                                           secondarySize );
1359         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1360         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1361     }
1363     {
1364         InkAction* inky = ink_action_new( "NodeLineAction",
1365                                           _("Node Line"),
1366                                           _("Make selected segments lines"),
1367                                           "node_line",
1368                                           secondarySize );
1369         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1370         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1371     }
1373     {
1374         InkAction* inky = ink_action_new( "NodeCurveAction",
1375                                           _("Node Curve"),
1376                                           _("Make selected segments curves"),
1377                                           "node_curve",
1378                                           secondarySize );
1379         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1380         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1381     }
1383     {
1384         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1385                                                       _("Show Handles"),
1386                                                       _("Show the Bezier handles of selected nodes"),
1387                                                       "nodes_show_handles",
1388                                                       Inkscape::ICON_SIZE_DECORATION );
1389         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1390         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1391         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1392     }
1394     {
1395         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1396                                                       _("Show Outline"),
1397                                                       _("Show the outline of the path"),
1398                                                       "nodes_show_helperpath",
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_helperpath), desktop );
1402         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1403     }
1405     {
1406         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1407                                           _("Next path effect parameter"),
1408                                           _("Show next path effect parameter for editing"),
1409                                           "edit_next_parameter",
1410                                           Inkscape::ICON_SIZE_DECORATION );
1411         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1412         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1413         g_object_set_data( holder, "nodes_lpeedit", inky);
1414     }
1416     {
1417         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1418                                           _("Edit clipping path"),
1419                                           _("Edit the clipping path of the object"),
1420                                           "nodeedit-clippath",
1421                                           Inkscape::ICON_SIZE_DECORATION );
1422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424         g_object_set_data( holder, "nodes_clippathedit", inky);
1425     }
1427     {
1428         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1429                                           _("Edit mask path"),
1430                                           _("Edit the mask of the object"),
1431                                           "nodeedit-mask",
1432                                           Inkscape::ICON_SIZE_DECORATION );
1433         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1434         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1435         g_object_set_data( holder, "nodes_maskedit", inky);
1436     }
1438     /* X coord of selected node(s) */
1439     {
1440         EgeAdjustmentAction* eact = 0;
1441         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1442         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1443         eact = create_adjustment_action( "NodeXAction",
1444                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1445                                          "/tools/nodes/Xcoord", 0,
1446                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1447                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1448                                          labels, values, G_N_ELEMENTS(labels),
1449                                          sp_node_path_x_value_changed );
1450         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1451         g_object_set_data( holder, "nodes_x_action", eact );
1452         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1453         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1454     }
1456     /* Y coord of selected node(s) */
1457     {
1458         EgeAdjustmentAction* eact = 0;
1459         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1460         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1461         eact = create_adjustment_action( "NodeYAction",
1462                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1463                                          "/tools/nodes/Ycoord", 0,
1464                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1465                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1466                                          labels, values, G_N_ELEMENTS(labels),
1467                                          sp_node_path_y_value_changed );
1468         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1469         g_object_set_data( holder, "nodes_y_action", eact );
1470         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1471         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1472     }
1474     // add the units menu
1475     {
1476         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1477         gtk_action_group_add_action( mainActions, act );
1478     }
1481     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1483     //watch selection
1484     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1486     sigc::connection *c_selection_changed =
1487         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1488                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1489     pool->add_connection ("selection-changed", c_selection_changed);
1491     sigc::connection *c_selection_modified =
1492         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1493                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1494     pool->add_connection ("selection-modified", c_selection_modified);
1496     sigc::connection *c_subselection_changed =
1497         new sigc::connection (desktop->connectToolSubselectionChanged
1498                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1499     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1501     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1503     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1504 } // end of sp_node_toolbox_prep()
1507 //########################
1508 //##    Zoom Toolbox    ##
1509 //########################
1511 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1513     // no custom GtkAction setup needed
1514 } // end of sp_zoom_toolbox_prep()
1516 void
1517 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1519     toolbox_set_desktop(toolbox,
1520                         desktop,
1521                         setup_tool_toolbox,
1522                         update_tool_toolbox,
1523                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1524                                                                          "event_context_connection")));
1528 void
1529 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1531     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1532                         desktop,
1533                         setup_aux_toolbox,
1534                         update_aux_toolbox,
1535                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1536                                                                          "event_context_connection")));
1539 void
1540 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1542     toolbox_set_desktop(toolbox,
1543                         desktop,
1544                         setup_commands_toolbox,
1545                         update_commands_toolbox,
1546                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1547                                                                          "event_context_connection")));
1550 static void
1551 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1553     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1554     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1556     if (old_desktop) {
1557         GList *children, *iter;
1559         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1560         for ( iter = children ; iter ; iter = iter->next ) {
1561             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1562         }
1563         g_list_free(children);
1564     }
1566     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1568     if (desktop) {
1569         gtk_widget_set_sensitive(toolbox, TRUE);
1570         setup_func(toolbox, desktop);
1571         update_func(desktop, desktop->event_context, toolbox);
1572         *conn = desktop->connectEventContextChanged
1573             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1574     } else {
1575         gtk_widget_set_sensitive(toolbox, FALSE);
1576     }
1578 } // end of toolbox_set_desktop()
1581 static void
1582 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1584     gchar const * descr =
1585         "<ui>"
1586         "  <toolbar name='ToolToolbar'>"
1587         "    <toolitem action='ToolSelector' />"
1588         "    <toolitem action='ToolNode' />"
1589         "    <toolitem action='ToolTweak' />"
1590         "    <toolitem action='ToolZoom' />"
1591         "    <toolitem action='ToolRect' />"
1592         "    <toolitem action='Tool3DBox' />"
1593         "    <toolitem action='ToolArc' />"
1594         "    <toolitem action='ToolStar' />"
1595         "    <toolitem action='ToolSpiral' />"
1596         "    <toolitem action='ToolPencil' />"
1597         "    <toolitem action='ToolPen' />"
1598         "    <toolitem action='ToolCalligraphic' />"
1599         "    <toolitem action='ToolEraser' />"
1600 //        "    <toolitem action='ToolLPETool' />"
1601         "    <toolitem action='ToolPaintBucket' />"
1602         "    <toolitem action='ToolText' />"
1603         "    <toolitem action='ToolConnector' />"
1604         "    <toolitem action='ToolGradient' />"
1605         "    <toolitem action='ToolDropper' />"
1606         "  </toolbar>"
1607         "</ui>";
1608     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1609     GtkUIManager* mgr = gtk_ui_manager_new();
1610     GError* errVal = 0;
1611     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1613     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1614     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1616     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1617     if ( prefs->getBool("/toolbox/icononly", true) ) {
1618         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1619     }
1620     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1621     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1623     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1624     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1626     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1628     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1629     if ( child ) {
1630         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1631     }
1633     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1634 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1638 static void
1639 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1641     gchar const *const tname = ( eventcontext
1642                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1643                                  : NULL );
1644     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1646     for (int i = 0 ; tools[i].type_name ; i++ ) {
1647         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1648         if ( act ) {
1649             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1650             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1651             if ( verbAct ) {
1652                 verbAct->set_active(setActive);
1653             }
1654         }
1655     }
1658 static void
1659 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1661     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1662     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1663     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1664     GtkUIManager* mgr = gtk_ui_manager_new();
1665     GError* errVal = 0;
1666     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1667     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1669     std::map<std::string, GtkWidget*> dataHolders;
1671     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1672         if ( aux_toolboxes[i].prep_func ) {
1673             // converted to GtkActions and UIManager
1675             GtkWidget* kludge = gtk_toolbar_new();
1676             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1677             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1678             dataHolders[aux_toolboxes[i].type_name] = kludge;
1679             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1680         } else {
1682             GtkWidget *sub_toolbox = 0;
1683             if (aux_toolboxes[i].create_func == NULL)
1684                 sub_toolbox = sp_empty_toolbox_new(desktop);
1685             else {
1686                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1687             }
1689             gtk_size_group_add_widget( grouper, sub_toolbox );
1691             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1692             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1694         }
1695     }
1697     // Second pass to create toolbars *after* all GtkActions are created
1698     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1699         if ( aux_toolboxes[i].prep_func ) {
1700             // converted to GtkActions and UIManager
1702             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1704             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1705             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1707             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1708             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1709             g_free( tmp );
1710             tmp = 0;
1712             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1713             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1714                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1715             }
1716             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1719             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1721             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1722                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1723                 swatch->setDesktop( desktop );
1724                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1725                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1726                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1727                 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 );
1728             }
1730             gtk_widget_show_all( holder );
1731             sp_set_font_size_smaller( holder );
1733             gtk_size_group_add_widget( grouper, holder );
1735             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1736             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1737         }
1738     }
1740     g_object_unref( G_OBJECT(grouper) );
1743 static void
1744 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1746     gchar const *tname = ( eventcontext
1747                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1748                            : NULL );
1749     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1750         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1751         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1752             gtk_widget_show_all(sub_toolbox);
1753             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1754         } else {
1755             gtk_widget_hide(sub_toolbox);
1756         }
1757     }
1760 static void
1761 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1763     gchar const * descr =
1764         "<ui>"
1765         "  <toolbar name='CommandsToolbar'>"
1766         "    <toolitem action='FileNew' />"
1767         "    <toolitem action='FileOpen' />"
1768         "    <toolitem action='FileSave' />"
1769         "    <toolitem action='FilePrint' />"
1770         "    <separator />"
1771         "    <toolitem action='FileImport' />"
1772         "    <toolitem action='FileExport' />"
1773         "    <separator />"
1774         "    <toolitem action='EditUndo' />"
1775         "    <toolitem action='EditRedo' />"
1776         "    <separator />"
1777         "    <toolitem action='EditCopy' />"
1778         "    <toolitem action='EditCut' />"
1779         "    <toolitem action='EditPaste' />"
1780         "    <separator />"
1781         "    <toolitem action='ZoomSelection' />"
1782         "    <toolitem action='ZoomDrawing' />"
1783         "    <toolitem action='ZoomPage' />"
1784         "    <separator />"
1785         "    <toolitem action='EditDuplicate' />"
1786         "    <toolitem action='EditClone' />"
1787         "    <toolitem action='EditUnlinkClone' />"
1788         "    <separator />"
1789         "    <toolitem action='SelectionGroup' />"
1790         "    <toolitem action='SelectionUnGroup' />"
1791         "    <separator />"
1792         "    <toolitem action='DialogFillStroke' />"
1793         "    <toolitem action='DialogText' />"
1794         "    <toolitem action='DialogXMLEditor' />"
1795         "    <toolitem action='DialogAlignDistribute' />"
1796         "    <separator />"
1797         "    <toolitem action='DialogPreferences' />"
1798         "    <toolitem action='DialogDocumentProperties' />"
1799         "  </toolbar>"
1800         "</ui>";
1801     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1802     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1804     GtkUIManager* mgr = gtk_ui_manager_new();
1805     GError* errVal = 0;
1807     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1808     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1810     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1811     if ( prefs->getBool("/toolbox/icononly", true) ) {
1812         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1813     }
1815     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1816     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1818     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1819     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1822     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1824     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1825     if ( child ) {
1826         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1827     }
1829     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1832 static void
1833 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1837 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1839     gtk_widget_show(toolbox_toplevel);
1840     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1842     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1843     if (!shown_toolbox) {
1844         return;
1845     }
1846     gtk_widget_show(toolbox);
1848     gtk_widget_show_all(shown_toolbox);
1851 static GtkWidget *
1852 sp_empty_toolbox_new(SPDesktop *desktop)
1854     GtkWidget *tbl = gtk_toolbar_new();
1855     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1856     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1858     gtk_widget_show_all(tbl);
1859     sp_set_font_size_smaller (tbl);
1861     return tbl;
1864 #define MODE_LABEL_WIDTH 70
1866 //########################
1867 //##       Star         ##
1868 //########################
1870 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1872     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1874     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1875         // do not remember prefs if this call is initiated by an undo change, because undoing object
1876         // creation sets bogus values to its attributes before it is deleted
1877         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1878         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
1879     }
1881     // quit if run by the attr_changed listener
1882     if (g_object_get_data( dataKludge, "freeze" )) {
1883         return;
1884     }
1886     // in turn, prevent listener from responding
1887     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1889     bool modmade = false;
1891     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1892     GSList const *items = selection->itemList();
1893     for (; items != NULL; items = items->next) {
1894         if (SP_IS_STAR((SPItem *) items->data)) {
1895             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1896             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1897             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1898                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1899                                     + M_PI / (gint)adj->value));
1900             SP_OBJECT((SPItem *) items->data)->updateRepr();
1901             modmade = true;
1902         }
1903     }
1904     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1905                                    _("Star: Change number of corners"));
1907     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1910 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1912     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1914     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1915         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1916         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
1917     }
1919     // quit if run by the attr_changed listener
1920     if (g_object_get_data( dataKludge, "freeze" )) {
1921         return;
1922     }
1924     // in turn, prevent listener from responding
1925     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1927     bool modmade = false;
1928     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1929     GSList const *items = selection->itemList();
1930     for (; items != NULL; items = items->next) {
1931         if (SP_IS_STAR((SPItem *) items->data)) {
1932             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1934             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1935             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1936             if (r2 < r1) {
1937                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1938             } else {
1939                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1940             }
1942             SP_OBJECT((SPItem *) items->data)->updateRepr();
1943             modmade = true;
1944         }
1945     }
1947     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1948                                    _("Star: Change spoke ratio"));
1950     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1953 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1955     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1956     bool flat = ege_select_one_action_get_active( act ) == 0;
1958     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1959         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1960         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
1961     }
1963     // quit if run by the attr_changed listener
1964     if (g_object_get_data( dataKludge, "freeze" )) {
1965         return;
1966     }
1968     // in turn, prevent listener from responding
1969     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1971     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1972     GSList const *items = selection->itemList();
1973     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1974     bool modmade = false;
1976     if ( prop_action ) {
1977         gtk_action_set_sensitive( prop_action, !flat );
1978     }
1980     for (; items != NULL; items = items->next) {
1981         if (SP_IS_STAR((SPItem *) items->data)) {
1982             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1983             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1984             SP_OBJECT((SPItem *) items->data)->updateRepr();
1985             modmade = true;
1986         }
1987     }
1989     if (modmade) {
1990         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1991                          flat ? _("Make polygon") : _("Make star"));
1992     }
1994     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1997 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1999     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2001     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2002         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2003         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2004     }
2006     // quit if run by the attr_changed listener
2007     if (g_object_get_data( dataKludge, "freeze" )) {
2008         return;
2009     }
2011     // in turn, prevent listener from responding
2012     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2014     bool modmade = false;
2016     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2017     GSList const *items = selection->itemList();
2018     for (; items != NULL; items = items->next) {
2019         if (SP_IS_STAR((SPItem *) items->data)) {
2020             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2021             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2022             SP_OBJECT(items->data)->updateRepr();
2023             modmade = true;
2024         }
2025     }
2026     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2027                                    _("Star: Change rounding"));
2029     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2032 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2034     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2036     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2037         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2038         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2039     }
2041     // quit if run by the attr_changed listener
2042     if (g_object_get_data( dataKludge, "freeze" )) {
2043         return;
2044     }
2046     // in turn, prevent listener from responding
2047     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2049     bool modmade = false;
2051     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2052     GSList const *items = selection->itemList();
2053     for (; items != NULL; items = items->next) {
2054         if (SP_IS_STAR((SPItem *) items->data)) {
2055             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2056             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2057             SP_OBJECT(items->data)->updateRepr();
2058             modmade = true;
2059         }
2060     }
2061     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2062                                    _("Star: Change randomization"));
2064     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2068 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2069                                        gchar const */*old_value*/, gchar const */*new_value*/,
2070                                        bool /*is_interactive*/, gpointer data)
2072     GtkWidget *tbl = GTK_WIDGET(data);
2074     // quit if run by the _changed callbacks
2075     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2076         return;
2077     }
2079     // in turn, prevent callbacks from responding
2080     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2082     GtkAdjustment *adj = 0;
2084     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2085     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2087     if (!strcmp(name, "inkscape:randomized")) {
2088         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2089         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2090     } else if (!strcmp(name, "inkscape:rounded")) {
2091         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2092         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2093     } else if (!strcmp(name, "inkscape:flatsided")) {
2094         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2095         char const *flatsides = repr->attribute("inkscape:flatsided");
2096         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2097         if ( flatsides && !strcmp(flatsides,"false") ) {
2098             ege_select_one_action_set_active( flat_action, 1 );
2099             gtk_action_set_sensitive( prop_action, TRUE );
2100         } else {
2101             ege_select_one_action_set_active( flat_action, 0 );
2102             gtk_action_set_sensitive( prop_action, FALSE );
2103         }
2104     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2105         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2106         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2107         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2108         if (r2 < r1) {
2109             gtk_adjustment_set_value(adj, r2/r1);
2110         } else {
2111             gtk_adjustment_set_value(adj, r1/r2);
2112         }
2113     } else if (!strcmp(name, "sodipodi:sides")) {
2114         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2115         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2116     }
2118     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2122 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2124     NULL, /* child_added */
2125     NULL, /* child_removed */
2126     star_tb_event_attr_changed,
2127     NULL, /* content_changed */
2128     NULL  /* order_changed */
2129 };
2132 /**
2133  *  \param selection Should not be NULL.
2134  */
2135 static void
2136 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2138     int n_selected = 0;
2139     Inkscape::XML::Node *repr = NULL;
2141     purge_repr_listener( tbl, tbl );
2143     for (GSList const *items = selection->itemList();
2144          items != NULL;
2145          items = items->next)
2146     {
2147         if (SP_IS_STAR((SPItem *) items->data)) {
2148             n_selected++;
2149             repr = SP_OBJECT_REPR((SPItem *) items->data);
2150         }
2151     }
2153     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2155     if (n_selected == 0) {
2156         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2157     } else if (n_selected == 1) {
2158         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2160         if (repr) {
2161             g_object_set_data( tbl, "repr", repr );
2162             Inkscape::GC::anchor(repr);
2163             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2164             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2165         }
2166     } else {
2167         // FIXME: implement averaging of all parameters for multiple selected stars
2168         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2169         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2170     }
2174 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2176     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2177     // callbacks to lump all the changes for all selected objects in one undo step
2179     GtkAdjustment *adj = 0;
2181     // fixme: make settable in prefs!
2182     gint mag = 5;
2183     gdouble prop = 0.5;
2184     gboolean flat = FALSE;
2185     gdouble randomized = 0;
2186     gdouble rounded = 0;
2188     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2189     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2191     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2192     gtk_action_set_sensitive( sb2, !flat );
2194     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2195     gtk_adjustment_set_value(adj, mag);
2196     gtk_adjustment_value_changed(adj);
2198     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2199     gtk_adjustment_set_value(adj, prop);
2200     gtk_adjustment_value_changed(adj);
2202     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2203     gtk_adjustment_set_value(adj, rounded);
2204     gtk_adjustment_value_changed(adj);
2206     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2207     gtk_adjustment_set_value(adj, randomized);
2208     gtk_adjustment_value_changed(adj);
2212 void
2213 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2215     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2216     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2217     GtkWidget *l = gtk_label_new(NULL);
2218     gtk_label_set_markup(GTK_LABEL(l), title);
2219     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2220     if ( GTK_IS_TOOLBAR(tbl) ) {
2221         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2222     } else {
2223         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2224     }
2225     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2229 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2231     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2233     {
2234         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2235         ege_output_action_set_use_markup( act, TRUE );
2236         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2237         g_object_set_data( holder, "mode_action", act );
2238     }
2240     {
2241         EgeAdjustmentAction* eact = 0;
2242         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2243         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2245         /* Flatsided checkbox */
2246         {
2247             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2249             GtkTreeIter iter;
2250             gtk_list_store_append( model, &iter );
2251             gtk_list_store_set( model, &iter,
2252                                 0, _("Polygon"),
2253                                 1, _("Regular polygon (with one handle) instead of a star"),
2254                                 2, "star_flat",
2255                                 -1 );
2257             gtk_list_store_append( model, &iter );
2258             gtk_list_store_set( model, &iter,
2259                                 0, _("Star"),
2260                                 1, _("Star instead of a regular polygon (with one handle)"),
2261                                 2, "star_angled",
2262                                 -1 );
2264             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2265             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2266             g_object_set_data( holder, "flat_action", act );
2268             ege_select_one_action_set_appearance( act, "full" );
2269             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2270             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2271             ege_select_one_action_set_icon_column( act, 2 );
2272             ege_select_one_action_set_icon_size( act, secondarySize );
2273             ege_select_one_action_set_tooltip_column( act, 1  );
2275             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2276             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2277         }
2279         /* Magnitude */
2280         {
2281         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2282         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2283         eact = create_adjustment_action( "MagnitudeAction",
2284                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2285                                          "/tools/shapes/star/magnitude", 3,
2286                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2287                                          3, 1024, 1, 5,
2288                                          labels, values, G_N_ELEMENTS(labels),
2289                                          sp_stb_magnitude_value_changed,
2290                                          1.0, 0 );
2291         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2292         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2293         }
2295         /* Spoke ratio */
2296         {
2297         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2298         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2299         eact = create_adjustment_action( "SpokeAction",
2300                                          _("Spoke ratio"), _("Spoke ratio:"),
2301                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2302                                          // Base radius is the same for the closest handle.
2303                                          _("Base radius to tip radius ratio"),
2304                                          "/tools/shapes/star/proportion", 0.5,
2305                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2306                                          0.01, 1.0, 0.01, 0.1,
2307                                          labels, values, G_N_ELEMENTS(labels),
2308                                          sp_stb_proportion_value_changed );
2309         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2310         g_object_set_data( holder, "prop_action", eact );
2311         }
2313         if ( !isFlatSided ) {
2314             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2315         } else {
2316             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2317         }
2319         /* Roundedness */
2320         {
2321         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2322         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2323         eact = create_adjustment_action( "RoundednessAction",
2324                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2325                                          "/tools/shapes/star/rounded", 0.0,
2326                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2327                                          -10.0, 10.0, 0.01, 0.1,
2328                                          labels, values, G_N_ELEMENTS(labels),
2329                                          sp_stb_rounded_value_changed );
2330         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2331         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2332         }
2334         /* Randomization */
2335         {
2336         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2337         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2338         eact = create_adjustment_action( "RandomizationAction",
2339                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2340                                          "/tools/shapes/star/randomized", 0.0,
2341                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2342                                          -10.0, 10.0, 0.001, 0.01,
2343                                          labels, values, G_N_ELEMENTS(labels),
2344                                          sp_stb_randomized_value_changed, 0.1, 3 );
2345         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2346         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2347         }
2348     }
2350     {
2351         /* Reset */
2352         {
2353             GtkAction* act = gtk_action_new( "StarResetAction",
2354                                              _("Defaults"),
2355                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2356                                              GTK_STOCK_CLEAR );
2357             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2358             gtk_action_group_add_action( mainActions, act );
2359             gtk_action_set_sensitive( act, TRUE );
2360         }
2361     }
2363     sigc::connection *connection = new sigc::connection(
2364         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2365         );
2366     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2367     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2371 //########################
2372 //##       Rect         ##
2373 //########################
2375 static void sp_rtb_sensitivize( GObject *tbl )
2377     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2378     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2379     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2381     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2382         gtk_action_set_sensitive( not_rounded, FALSE );
2383     } else {
2384         gtk_action_set_sensitive( not_rounded, TRUE );
2385     }
2389 static void
2390 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2391                           void (*setter)(SPRect *, gdouble))
2393     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2395     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2396     SPUnit const *unit = tracker->getActiveUnit();
2398     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2399         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2400         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2401     }
2403     // quit if run by the attr_changed listener
2404     if (g_object_get_data( tbl, "freeze" )) {
2405         return;
2406     }
2408     // in turn, prevent listener from responding
2409     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2411     bool modmade = false;
2412     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2413     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2414         if (SP_IS_RECT(items->data)) {
2415             if (adj->value != 0) {
2416                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2417             } else {
2418                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2419             }
2420             modmade = true;
2421         }
2422     }
2424     sp_rtb_sensitivize( tbl );
2426     if (modmade) {
2427         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2428                                    _("Change rectangle"));
2429     }
2431     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2434 static void
2435 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2437     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2440 static void
2441 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2443     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2446 static void
2447 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2449     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2452 static void
2453 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2455     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2460 static void
2461 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2463     GtkAdjustment *adj = 0;
2465     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2466     gtk_adjustment_set_value(adj, 0.0);
2467     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2468     gtk_adjustment_value_changed(adj);
2470     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2471     gtk_adjustment_set_value(adj, 0.0);
2472     gtk_adjustment_value_changed(adj);
2474     sp_rtb_sensitivize( obj );
2477 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2478                                        gchar const */*old_value*/, gchar const */*new_value*/,
2479                                        bool /*is_interactive*/, gpointer data)
2481     GObject *tbl = G_OBJECT(data);
2483     // quit if run by the _changed callbacks
2484     if (g_object_get_data( tbl, "freeze" )) {
2485         return;
2486     }
2488     // in turn, prevent callbacks from responding
2489     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2491     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2492     SPUnit const *unit = tracker->getActiveUnit();
2494     gpointer item = g_object_get_data( tbl, "item" );
2495     if (item && SP_IS_RECT(item)) {
2496         {
2497             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2498             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2499             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2500         }
2502         {
2503             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2504             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2505             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2506         }
2508         {
2509             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2510             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2511             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2512         }
2514         {
2515             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2516             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2517             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2518         }
2519     }
2521     sp_rtb_sensitivize( tbl );
2523     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2527 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2528     NULL, /* child_added */
2529     NULL, /* child_removed */
2530     rect_tb_event_attr_changed,
2531     NULL, /* content_changed */
2532     NULL  /* order_changed */
2533 };
2535 /**
2536  *  \param selection should not be NULL.
2537  */
2538 static void
2539 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2541     int n_selected = 0;
2542     Inkscape::XML::Node *repr = NULL;
2543     SPItem *item = NULL;
2545     if ( g_object_get_data( tbl, "repr" ) ) {
2546         g_object_set_data( tbl, "item", NULL );
2547     }
2548     purge_repr_listener( tbl, tbl );
2550     for (GSList const *items = selection->itemList();
2551          items != NULL;
2552          items = items->next) {
2553         if (SP_IS_RECT((SPItem *) items->data)) {
2554             n_selected++;
2555             item = (SPItem *) items->data;
2556             repr = SP_OBJECT_REPR(item);
2557         }
2558     }
2560     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2562     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2564     if (n_selected == 0) {
2565         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2567         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2568         gtk_action_set_sensitive(w, FALSE);
2569         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2570         gtk_action_set_sensitive(h, FALSE);
2572     } else if (n_selected == 1) {
2573         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2574         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2576         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2577         gtk_action_set_sensitive(w, TRUE);
2578         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2579         gtk_action_set_sensitive(h, TRUE);
2581         if (repr) {
2582             g_object_set_data( tbl, "repr", repr );
2583             g_object_set_data( tbl, "item", item );
2584             Inkscape::GC::anchor(repr);
2585             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2586             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2587         }
2588     } else {
2589         // FIXME: implement averaging of all parameters for multiple selected
2590         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2591         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2592         sp_rtb_sensitivize( tbl );
2593     }
2597 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2599     EgeAdjustmentAction* eact = 0;
2600     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2602     {
2603         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2604         ege_output_action_set_use_markup( act, TRUE );
2605         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2606         g_object_set_data( holder, "mode_action", act );
2607     }
2609     // rx/ry units menu: create
2610     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2611     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2612     // fixme: add % meaning per cent of the width/height
2613     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2614     g_object_set_data( holder, "tracker", tracker );
2616     /* W */
2617     {
2618         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2619         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2620         eact = create_adjustment_action( "RectWidthAction",
2621                                          _("Width"), _("W:"), _("Width of rectangle"),
2622                                          "/tools/shapes/rect/width", 0,
2623                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2624                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2625                                          labels, values, G_N_ELEMENTS(labels),
2626                                          sp_rtb_width_value_changed );
2627         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2628         g_object_set_data( holder, "width_action", eact );
2629         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2630         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2631     }
2633     /* H */
2634     {
2635         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2636         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2637         eact = create_adjustment_action( "RectHeightAction",
2638                                          _("Height"), _("H:"), _("Height of rectangle"),
2639                                          "/tools/shapes/rect/height", 0,
2640                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2641                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2642                                          labels, values, G_N_ELEMENTS(labels),
2643                                          sp_rtb_height_value_changed );
2644         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2645         g_object_set_data( holder, "height_action", eact );
2646         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2647         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2648     }
2650     /* rx */
2651     {
2652         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2653         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2654         eact = create_adjustment_action( "RadiusXAction",
2655                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2656                                          "/tools/shapes/rect/rx", 0,
2657                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2658                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2659                                          labels, values, G_N_ELEMENTS(labels),
2660                                          sp_rtb_rx_value_changed);
2661         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2662         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2663     }
2665     /* ry */
2666     {
2667         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2668         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2669         eact = create_adjustment_action( "RadiusYAction",
2670                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2671                                          "/tools/shapes/rect/ry", 0,
2672                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2673                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2674                                          labels, values, G_N_ELEMENTS(labels),
2675                                          sp_rtb_ry_value_changed);
2676         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2677         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2678     }
2680     // add the units menu
2681     {
2682         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2683         gtk_action_group_add_action( mainActions, act );
2684     }
2686     /* Reset */
2687     {
2688         InkAction* inky = ink_action_new( "RectResetAction",
2689                                           _("Not rounded"),
2690                                           _("Make corners sharp"),
2691                                           "squared_corner",
2692                                           secondarySize );
2693         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2694         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2695         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2696         g_object_set_data( holder, "not_rounded", inky );
2697     }
2699     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2700     sp_rtb_sensitivize( holder );
2702     sigc::connection *connection = new sigc::connection(
2703         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2704         );
2705     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2706     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2709 //########################
2710 //##       3D Box       ##
2711 //########################
2713 // normalize angle so that it lies in the interval [0,360]
2714 static double box3d_normalize_angle (double a) {
2715     double angle = a + ((int) (a/360.0))*360;
2716     if (angle < 0) {
2717         angle += 360.0;
2718     }
2719     return angle;
2722 static void
2723 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2724                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2725     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2726     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2727     //       are reset).
2728     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2730     if (is_infinite) {
2731         gtk_toggle_action_set_active(tact, TRUE);
2732         gtk_action_set_sensitive(act, TRUE);
2734         double angle = persp3d_get_infinite_angle(persp, axis);
2735         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2736             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2737         }
2738     } else {
2739         gtk_toggle_action_set_active(tact, FALSE);
2740         gtk_action_set_sensitive(act, FALSE);
2741     }
2744 static void
2745 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2746     if (!persp_repr) {
2747         g_print ("No perspective given to box3d_resync_toolbar().\n");
2748         return;
2749     }
2751     GtkWidget *tbl = GTK_WIDGET(data);
2752     GtkAdjustment *adj = 0;
2753     GtkAction *act = 0;
2754     GtkToggleAction *tact = 0;
2755     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2756     {
2757         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2758         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2759         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2761         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2762     }
2763     {
2764         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2765         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2766         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2768         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2769     }
2770     {
2771         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2772         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2773         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2775         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2776     }
2779 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2780                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2781                                                   bool /*is_interactive*/, gpointer data)
2783     GtkWidget *tbl = GTK_WIDGET(data);
2785     // quit if run by the attr_changed or selection changed listener
2786     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2787         return;
2788     }
2790     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2791     // sp_document_maybe_done() when the document is undo insensitive)
2792     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2794     // TODO: Only update the appropriate part of the toolbar
2795 //    if (!strcmp(name, "inkscape:vp_z")) {
2796         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2797 //    }
2799     Persp3D *persp = persp3d_get_from_repr(repr);
2800     persp3d_update_box_reprs(persp);
2802     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2805 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2807     NULL, /* child_added */
2808     NULL, /* child_removed */
2809     box3d_persp_tb_event_attr_changed,
2810     NULL, /* content_changed */
2811     NULL  /* order_changed */
2812 };
2814 /**
2815  *  \param selection Should not be NULL.
2816  */
2817 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2818 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2819 static void
2820 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2822     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2823     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2824     // update the perspectives with infinite VPs and leave the other ones untouched).
2826     Inkscape::XML::Node *persp_repr = NULL;
2827     purge_repr_listener(tbl, tbl);
2829     SPItem *item = selection->singleItem();
2830     if (item && SP_IS_BOX3D(item)) {
2831         // FIXME: Also deal with multiple selected boxes
2832         SPBox3D *box = SP_BOX3D(item);
2833         Persp3D *persp = box3d_get_perspective(box);
2834         persp_repr = SP_OBJECT_REPR(persp);
2835         if (persp_repr) {
2836             g_object_set_data(tbl, "repr", persp_repr);
2837             Inkscape::GC::anchor(persp_repr);
2838             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2839             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2840         }
2842         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2843         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2844         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
2846         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
2847         box3d_resync_toolbar(persp_repr, tbl);
2848         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
2849     }
2852 static void
2853 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2855     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2856     SPDocument *document = sp_desktop_document(desktop);
2858     // quit if run by the attr_changed or selection changed listener
2859     if (g_object_get_data( dataKludge, "freeze" )) {
2860         return;
2861     }
2863     // in turn, prevent listener from responding
2864     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
2866     //Persp3D *persp = document->current_persp3d;
2867     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2868     if (sel_persps.empty()) {
2869         // this can happen when the document is created; we silently ignore it
2870         return;
2871     }
2872     Persp3D *persp = sel_persps.front();
2874     persp->tmat.set_infinite_direction (axis, adj->value);
2875     SP_OBJECT(persp)->updateRepr();
2877     // TODO: use the correct axis here, too
2878     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2880     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2884 static void
2885 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2887     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2890 static void
2891 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2893     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2896 static void
2897 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2899     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2903 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2905     // TODO: Take all selected perspectives into account
2906     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2907     if (sel_persps.empty()) {
2908         // this can happen when the document is created; we silently ignore it
2909         return;
2910     }
2911     Persp3D *persp = sel_persps.front();
2913     bool set_infinite = gtk_toggle_action_get_active(act);
2914     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2917 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2919     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2922 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2924     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2927 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2929     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2932 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2934     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2935     EgeAdjustmentAction* eact = 0;
2936     SPDocument *document = sp_desktop_document (desktop);
2937     Persp3D *persp = document->current_persp3d;
2939     EgeAdjustmentAction* box3d_angle_x = 0;
2940     EgeAdjustmentAction* box3d_angle_y = 0;
2941     EgeAdjustmentAction* box3d_angle_z = 0;
2943     /* Angle X */
2944     {
2945         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2946         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2947         eact = create_adjustment_action( "3DBoxAngleXAction",
2948                                          _("Angle in X direction"), _("Angle X:"),
2949                                          // Translators: PL is short for 'perspective line'
2950                                          _("Angle of PLs in X direction"),
2951                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
2952                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2953                                          -360.0, 360.0, 1.0, 10.0,
2954                                          labels, values, G_N_ELEMENTS(labels),
2955                                          box3d_angle_x_value_changed );
2956         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2957         g_object_set_data( holder, "box3d_angle_x_action", eact );
2958         box3d_angle_x = eact;
2959     }
2961     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2962         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2963     } else {
2964         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2965     }
2968     /* VP X state */
2969     {
2970         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2971                                                       // Translators: VP is short for 'vanishing point'
2972                                                       _("State of VP in X direction"),
2973                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2974                                                       "toggle_vp_x",
2975                                                       Inkscape::ICON_SIZE_DECORATION );
2976         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2977         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2978         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2979         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
2980         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
2981     }
2983     /* Angle Y */
2984     {
2985         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2986         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2987         eact = create_adjustment_action( "3DBoxAngleYAction",
2988                                          _("Angle in Y direction"), _("Angle Y:"),
2989                                          // Translators: PL is short for 'perspective line'
2990                                          _("Angle of PLs in Y direction"),
2991                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
2992                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2993                                          -360.0, 360.0, 1.0, 10.0,
2994                                          labels, values, G_N_ELEMENTS(labels),
2995                                          box3d_angle_y_value_changed );
2996         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2997         g_object_set_data( holder, "box3d_angle_y_action", eact );
2998         box3d_angle_y = eact;
2999     }
3001     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3002         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3003     } else {
3004         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3005     }
3007     /* VP Y state */
3008     {
3009         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3010                                                       // Translators: VP is short for 'vanishing point'
3011                                                       _("State of VP in Y direction"),
3012                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3013                                                       "toggle_vp_y",
3014                                                       Inkscape::ICON_SIZE_DECORATION );
3015         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3016         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3017         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3018         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3019         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3020     }
3022     /* Angle Z */
3023     {
3024         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3025         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3026         eact = create_adjustment_action( "3DBoxAngleZAction",
3027                                          _("Angle in Z direction"), _("Angle Z:"),
3028                                          // Translators: PL is short for 'perspective line'
3029                                          _("Angle of PLs in Z direction"),
3030                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3031                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3032                                          -360.0, 360.0, 1.0, 10.0,
3033                                          labels, values, G_N_ELEMENTS(labels),
3034                                          box3d_angle_z_value_changed );
3035         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3036         g_object_set_data( holder, "box3d_angle_z_action", eact );
3037         box3d_angle_z = eact;
3038     }
3040     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3041         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3042     } else {
3043         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3044     }
3046     /* VP Z state */
3047     {
3048         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3049                                                       // Translators: VP is short for 'vanishing point'
3050                                                       _("State of VP in Z direction"),
3051                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3052                                                       "toggle_vp_z",
3053                                                       Inkscape::ICON_SIZE_DECORATION );
3054         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3055         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3056         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3057         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3058         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3059     }
3061     sigc::connection *connection = new sigc::connection(
3062         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3063        );
3064     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3065     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3068 //########################
3069 //##       Spiral       ##
3070 //########################
3072 static void
3073 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3075     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3077     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3078         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3079         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3080     }
3082     // quit if run by the attr_changed listener
3083     if (g_object_get_data( tbl, "freeze" )) {
3084         return;
3085     }
3087     // in turn, prevent listener from responding
3088     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3090     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3092     bool modmade = false;
3093     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3094          items != NULL;
3095          items = items->next)
3096     {
3097         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3098             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3099             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3100             SP_OBJECT((SPItem *) items->data)->updateRepr();
3101             modmade = true;
3102         }
3103     }
3105     g_free(namespaced_name);
3107     if (modmade) {
3108         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3109                                    _("Change spiral"));
3110     }
3112     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3115 static void
3116 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3118     sp_spl_tb_value_changed(adj, tbl, "revolution");
3121 static void
3122 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3124     sp_spl_tb_value_changed(adj, tbl, "expansion");
3127 static void
3128 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3130     sp_spl_tb_value_changed(adj, tbl, "t0");
3133 static void
3134 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3136     GtkWidget *tbl = GTK_WIDGET(obj);
3138     GtkAdjustment *adj;
3140     // fixme: make settable
3141     gdouble rev = 5;
3142     gdouble exp = 1.0;
3143     gdouble t0 = 0.0;
3145     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3146     gtk_adjustment_set_value(adj, rev);
3147     gtk_adjustment_value_changed(adj);
3149     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3150     gtk_adjustment_set_value(adj, exp);
3151     gtk_adjustment_value_changed(adj);
3153     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3154     gtk_adjustment_set_value(adj, t0);
3155     gtk_adjustment_value_changed(adj);
3157     spinbutton_defocus(GTK_OBJECT(tbl));
3161 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3162                                          gchar const */*old_value*/, gchar const */*new_value*/,
3163                                          bool /*is_interactive*/, gpointer data)
3165     GtkWidget *tbl = GTK_WIDGET(data);
3167     // quit if run by the _changed callbacks
3168     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3169         return;
3170     }
3172     // in turn, prevent callbacks from responding
3173     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3175     GtkAdjustment *adj;
3176     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3177     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3179     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3180     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3182     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3183     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3185     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3189 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3190     NULL, /* child_added */
3191     NULL, /* child_removed */
3192     spiral_tb_event_attr_changed,
3193     NULL, /* content_changed */
3194     NULL  /* order_changed */
3195 };
3197 static void
3198 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3200     int n_selected = 0;
3201     Inkscape::XML::Node *repr = NULL;
3203     purge_repr_listener( tbl, tbl );
3205     for (GSList const *items = selection->itemList();
3206          items != NULL;
3207          items = items->next)
3208     {
3209         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3210             n_selected++;
3211             repr = SP_OBJECT_REPR((SPItem *) items->data);
3212         }
3213     }
3215     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3217     if (n_selected == 0) {
3218         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3219     } else if (n_selected == 1) {
3220         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3222         if (repr) {
3223             g_object_set_data( tbl, "repr", repr );
3224             Inkscape::GC::anchor(repr);
3225             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3226             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3227         }
3228     } else {
3229         // FIXME: implement averaging of all parameters for multiple selected
3230         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3231         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3232     }
3236 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3238     EgeAdjustmentAction* eact = 0;
3239     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3241     {
3242         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3243         ege_output_action_set_use_markup( act, TRUE );
3244         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3245         g_object_set_data( holder, "mode_action", act );
3246     }
3248     /* Revolution */
3249     {
3250         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3251         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3252         eact = create_adjustment_action( "SpiralRevolutionAction",
3253                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3254                                          "/tools/shapes/spiral/revolution", 3.0,
3255                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3256                                          0.01, 1024.0, 0.1, 1.0,
3257                                          labels, values, G_N_ELEMENTS(labels),
3258                                          sp_spl_tb_revolution_value_changed, 1, 2);
3259         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3260     }
3262     /* Expansion */
3263     {
3264         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3265         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3266         eact = create_adjustment_action( "SpiralExpansionAction",
3267                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3268                                          "/tools/shapes/spiral/expansion", 1.0,
3269                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3270                                          0.0, 1000.0, 0.01, 1.0,
3271                                          labels, values, G_N_ELEMENTS(labels),
3272                                          sp_spl_tb_expansion_value_changed);
3273         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3274     }
3276     /* T0 */
3277     {
3278         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3279         gdouble values[] = {0, 0.5, 0.9};
3280         eact = create_adjustment_action( "SpiralT0Action",
3281                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3282                                          "/tools/shapes/spiral/t0", 0.0,
3283                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3284                                          0.0, 0.999, 0.01, 1.0,
3285                                          labels, values, G_N_ELEMENTS(labels),
3286                                          sp_spl_tb_t0_value_changed);
3287         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3288     }
3290     /* Reset */
3291     {
3292         InkAction* inky = ink_action_new( "SpiralResetAction",
3293                                           _("Defaults"),
3294                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3295                                           GTK_STOCK_CLEAR,
3296                                           secondarySize );
3297         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3298         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3299     }
3302     sigc::connection *connection = new sigc::connection(
3303         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3304         );
3305     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3306     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3309 //########################
3310 //##     Pen/Pencil     ##
3311 //########################
3313 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3314 static Glib::ustring const
3315 freehand_tool_name(GObject *dataKludge)
3317     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3318     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3319              ? "/tools/freehand/pen"
3320              : "/tools/freehand/pencil" );
3323 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3325     gint mode = ege_select_one_action_get_active(act);
3327     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3328     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3330     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3332     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3333     // preparatory work here
3334     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3335         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3336         sp_pen_context_set_polyline_mode(pc);
3337     }
3340 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3342     /* Freehand mode toggle buttons */
3343     {
3344         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3345         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3346         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3348         {
3349             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3351             GtkTreeIter iter;
3352             gtk_list_store_append( model, &iter );
3353             gtk_list_store_set( model, &iter,
3354                                 0, _("Bezier"),
3355                                 1, _("Create regular Bezier path"),
3356                                 2, "bezier_mode",
3357                                 -1 );
3359             gtk_list_store_append( model, &iter );
3360             gtk_list_store_set( model, &iter,
3361                                 0, _("Spiro"),
3362                                 1, _("Create Spiro path"),
3363                                 2, "spiro_splines_mode",
3364                                 -1 );
3366             if (!tool_is_pencil) {
3367                 gtk_list_store_append( model, &iter );
3368                 gtk_list_store_set( model, &iter,
3369                                     0, _("Zigzag"),
3370                                     1, _("Create a sequence of straight line segments"),
3371                                     2, "polylines_mode",
3372                                     -1 );
3374                 gtk_list_store_append( model, &iter );
3375                 gtk_list_store_set( model, &iter,
3376                                     0, _("Paraxial"),
3377                                     1, _("Create a sequence of paraxial line segments"),
3378                                     2, "paraxial_lines_mode",
3379                                     -1 );
3380             }
3382             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3383                                                                 "FreehandModeActionPencil" :
3384                                                                 "FreehandModeActionPen",
3385                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3386             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3388             ege_select_one_action_set_appearance( act, "full" );
3389             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3390             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3391             ege_select_one_action_set_icon_column( act, 2 );
3392             ege_select_one_action_set_icon_size( act, secondarySize );
3393             ege_select_one_action_set_tooltip_column( act, 1  );
3395             ege_select_one_action_set_active( act, freehandMode);
3396             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3397         }
3398     }
3401 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3402     gint shape = ege_select_one_action_get_active( act );
3403     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3404     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3407 /**
3408  * \brief Generate the list of freehand advanced shape option entries.
3409  */
3410 GList * freehand_shape_dropdown_items_list() {
3411     GList *glist = NULL;
3413     glist = g_list_append (glist, _("None"));
3414     glist = g_list_append (glist, _("Triangle in"));
3415     glist = g_list_append (glist, _("Triangle out"));
3416     glist = g_list_append (glist, _("Ellipse"));
3417     glist = g_list_append (glist, _("From clipboard"));
3419     return glist;
3422 static void
3423 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3424     /*advanced shape options */
3425     {
3426         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3427         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3429         GList* items = 0;
3430         gint count = 0;
3431         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3432         {
3433             GtkTreeIter iter;
3434             gtk_list_store_append( model, &iter );
3435             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3436             count++;
3437         }
3438         g_list_free( items );
3439         items = 0;
3440         EgeSelectOneAction* act1 = ege_select_one_action_new(
3441             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3442             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3443         g_object_set( act1, "short_label", _("Shape:"), NULL );
3444         ege_select_one_action_set_appearance( act1, "compact" );
3445         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3446         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3447         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3448         g_object_set_data( holder, "shape_action", act1 );
3449     }
3452 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3454     sp_add_freehand_mode_toggle(mainActions, holder, false);
3455     freehand_add_advanced_shape_options(mainActions, holder, false);
3459 static void
3460 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3462     GtkWidget *tbl = GTK_WIDGET(obj);
3464     GtkAdjustment *adj;
3466     // fixme: make settable
3467     gdouble tolerance = 4;
3469     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3470     gtk_adjustment_set_value(adj, tolerance);
3471     gtk_adjustment_value_changed(adj);
3473     spinbutton_defocus(GTK_OBJECT(tbl));
3476 static void
3477 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3479     // quit if run by the attr_changed listener
3480     if (g_object_get_data( tbl, "freeze" )) {
3481         return;
3482     }
3483     // in turn, prevent listener from responding
3484     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3485     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3486     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3487     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3490 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3491 public:
3492     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3493     {
3494         g_object_set_data(_obj, "prefobserver", this);
3495     }
3496     virtual ~PencilToleranceObserver() {
3497         if (g_object_get_data(_obj, "prefobserver") == this) {
3498                 g_object_set_data(_obj, "prefobserver", NULL);
3499         }
3500     }
3501     virtual void notify(Inkscape::Preferences::Entry const &val) {
3502         GObject* tbl = _obj;
3503         if (g_object_get_data( tbl, "freeze" )) {
3504             return;
3505         }
3506         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3508         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3510         double v = val.getDouble(adj->value);
3511         gtk_adjustment_set_value(adj, v);
3512         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3513     }
3514 private:
3515     GObject *_obj;
3516 };
3519 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3521     sp_add_freehand_mode_toggle(mainActions, holder, true);
3523     EgeAdjustmentAction* eact = 0;
3525     /* Tolerance */
3526     {
3527         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3528         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3529         eact = create_adjustment_action( "PencilToleranceAction",
3530                                          _("Smoothing:"), _("Smoothing: "),
3531                  _("How much smoothing (simplifying) is applied to the line"),
3532                                          "/tools/freehand/pencil/tolerance",
3533                                          3.0,
3534                                          GTK_WIDGET(desktop->canvas), NULL,
3535                                          holder, TRUE, "altx-pencil",
3536                                          1, 100.0, 0.5, 0,
3537                                          labels, values, G_N_ELEMENTS(labels),
3538                                          sp_pencil_tb_tolerance_value_changed,
3539                                          1, 2);
3540         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3541         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3543         PencilToleranceObserver *obs =
3544             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
3545     }
3547     /* advanced shape options */
3548     freehand_add_advanced_shape_options(mainActions, holder, true);
3550     /* Reset */
3551     {
3552         InkAction* inky = ink_action_new( "PencilResetAction",
3553                                           _("Defaults"),
3554                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3555                                           GTK_STOCK_CLEAR,
3556                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3557         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3558         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3559     }
3561     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3566 //########################
3567 //##       Tweak        ##
3568 //########################
3570 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3572     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3573     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
3576 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3578     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3579     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
3582 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3584     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3585     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
3588 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3590     int mode = ege_select_one_action_get_active( act );
3591     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3592     prefs->setInt("/tools/tweak/mode", mode);
3594     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3595     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3596     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3597     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3598     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3599     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3600     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3601         if (doh) gtk_action_set_sensitive (doh, TRUE);
3602         if (dos) gtk_action_set_sensitive (dos, TRUE);
3603         if (dol) gtk_action_set_sensitive (dol, TRUE);
3604         if (doo) gtk_action_set_sensitive (doo, TRUE);
3605         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3606         if (fid) gtk_action_set_sensitive (fid, FALSE);
3607     } else {
3608         if (doh) gtk_action_set_sensitive (doh, FALSE);
3609         if (dos) gtk_action_set_sensitive (dos, FALSE);
3610         if (dol) gtk_action_set_sensitive (dol, FALSE);
3611         if (doo) gtk_action_set_sensitive (doo, FALSE);
3612         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3613         if (fid) gtk_action_set_sensitive (fid, TRUE);
3614     }
3617 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3619     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3620     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
3623 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3624     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3625     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
3627 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3628     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3629     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
3631 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3632     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3633     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
3635 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3636     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3637     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
3640 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3642     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3643     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3645     {
3646         /* Width */
3647         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3648         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3649         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3650                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3651                                                               "/tools/tweak/width", 15,
3652                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3653                                                               1, 100, 1.0, 0.0,
3654                                                               labels, values, G_N_ELEMENTS(labels),
3655                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3656         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3657         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3658         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3659     }
3662     {
3663         /* Force */
3664         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3665         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3666         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3667                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3668                                                               "/tools/tweak/force", 20,
3669                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3670                                                               1, 100, 1.0, 0.0,
3671                                                               labels, values, G_N_ELEMENTS(labels),
3672                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3673         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3674         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3675         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3676     }
3678     /* Mode */
3679     {
3680         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3682         GtkTreeIter iter;
3683         gtk_list_store_append( model, &iter );
3684         gtk_list_store_set( model, &iter,
3685                             0, _("Move mode"),
3686                             1, _("Move objects in any direction"),
3687                             2, "tweak_move_mode",
3688                             -1 );
3690         gtk_list_store_append( model, &iter );
3691         gtk_list_store_set( model, &iter,
3692                             0, _("Move in/out mode"),
3693                             1, _("Move objects towards cursor; with Shift from cursor"),
3694                             2, "tweak_move_mode_inout",
3695                             -1 );
3697         gtk_list_store_append( model, &iter );
3698         gtk_list_store_set( model, &iter,
3699                             0, _("Move jitter mode"),
3700                             1, _("Move objects in random directions"),
3701                             2, "tweak_move_mode_jitter",
3702                             -1 );
3704         gtk_list_store_append( model, &iter );
3705         gtk_list_store_set( model, &iter,
3706                             0, _("Scale mode"),
3707                             1, _("Scale objects, with Shift scale up"),
3708                             2, "tweak_scale_mode",
3709                             -1 );
3711         gtk_list_store_append( model, &iter );
3712         gtk_list_store_set( model, &iter,
3713                             0, _("Rotate mode"),
3714                             1, _("Rotate objects, with Shift counterclockwise"),
3715                             2, "tweak_rotate_mode",
3716                             -1 );
3718         gtk_list_store_append( model, &iter );
3719         gtk_list_store_set( model, &iter,
3720                             0, _("Duplicate/delete mode"),
3721                             1, _("Duplicate objects, with Shift delete"),
3722                             2, "tweak_moreless_mode",
3723                             -1 );
3725         gtk_list_store_append( model, &iter );
3726         gtk_list_store_set( model, &iter,
3727                             0, _("Push mode"),
3728                             1, _("Push parts of paths in any direction"),
3729                             2, "tweak_push_mode",
3730                             -1 );
3732         gtk_list_store_append( model, &iter );
3733         gtk_list_store_set( model, &iter,
3734                             0, _("Shrink/grow mode"),
3735                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
3736                             2, "tweak_shrink_mode",
3737                             -1 );
3739         gtk_list_store_append( model, &iter );
3740         gtk_list_store_set( model, &iter,
3741                             0, _("Attract/repel mode"),
3742                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
3743                             2, "tweak_attract_mode",
3744                             -1 );
3746         gtk_list_store_append( model, &iter );
3747         gtk_list_store_set( model, &iter,
3748                             0, _("Roughen mode"),
3749                             1, _("Roughen parts of paths"),
3750                             2, "tweak_roughen_mode",
3751                             -1 );
3753         gtk_list_store_append( model, &iter );
3754         gtk_list_store_set( model, &iter,
3755                             0, _("Color paint mode"),
3756                             1, _("Paint the tool's color upon selected objects"),
3757                             2, "tweak_colorpaint_mode",
3758                             -1 );
3760         gtk_list_store_append( model, &iter );
3761         gtk_list_store_set( model, &iter,
3762                             0, _("Color jitter mode"),
3763                             1, _("Jitter the colors of selected objects"),
3764                             2, "tweak_colorjitter_mode",
3765                             -1 );
3767         gtk_list_store_append( model, &iter );
3768         gtk_list_store_set( model, &iter,
3769                             0, _("Blur mode"),
3770                             1, _("Blur selected objects more; with Shift, blur less"),
3771                             2, "tweak_blur_mode",
3772                             -1 );
3775         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3776         g_object_set( act, "short_label", _("Mode:"), NULL );
3777         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3778         g_object_set_data( holder, "mode_action", act );
3780         ege_select_one_action_set_appearance( act, "full" );
3781         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3782         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3783         ege_select_one_action_set_icon_column( act, 2 );
3784         ege_select_one_action_set_icon_size( act, secondarySize );
3785         ege_select_one_action_set_tooltip_column( act, 1  );
3787         gint mode = prefs->getInt("/tools/tweak/mode", 0);
3788         ege_select_one_action_set_active( act, mode );
3789         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3791         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3792     }
3794     guint mode = prefs->getInt("/tools/tweak/mode", 0);
3796     {
3797         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3798         ege_output_action_set_use_markup( act, TRUE );
3799         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3800         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3801             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3802         g_object_set_data( holder, "tweak_channels_label", act);
3803     }
3805     {
3806         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3807                                                       _("Hue"),
3808                                                       _("In color mode, act on objects' hue"),
3809                                                       NULL,
3810                                                       Inkscape::ICON_SIZE_DECORATION );
3811         //TRANSLATORS:  "H" here stands for hue
3812         g_object_set( act, "short_label", _("H"), NULL );
3813         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3814         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3815         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
3816         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3817             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3818         g_object_set_data( holder, "tweak_doh", act);
3819     }
3820     {
3821         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3822                                                       _("Saturation"),
3823                                                       _("In color mode, act on objects' saturation"),
3824                                                       NULL,
3825                                                       Inkscape::ICON_SIZE_DECORATION );
3826         //TRANSLATORS: "S" here stands for Saturation
3827         g_object_set( act, "short_label", _("S"), NULL );
3828         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3829         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3830         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
3831         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3832             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3833         g_object_set_data( holder, "tweak_dos", act );
3834     }
3835     {
3836         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3837                                                       _("Lightness"),
3838                                                       _("In color mode, act on objects' lightness"),
3839                                                       NULL,
3840                                                       Inkscape::ICON_SIZE_DECORATION );
3841         //TRANSLATORS: "L" here stands for Lightness
3842         g_object_set( act, "short_label", _("L"), NULL );
3843         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3844         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3845         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
3846         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3847             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3848         g_object_set_data( holder, "tweak_dol", act );
3849     }
3850     {
3851         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3852                                                       _("Opacity"),
3853                                                       _("In color mode, act on objects' opacity"),
3854                                                       NULL,
3855                                                       Inkscape::ICON_SIZE_DECORATION );
3856         //TRANSLATORS: "O" here stands for Opacity
3857         g_object_set( act, "short_label", _("O"), NULL );
3858         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3859         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3860         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
3861         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3862             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3863         g_object_set_data( holder, "tweak_doo", act );
3864     }
3866     {   /* Fidelity */
3867         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3868         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3869         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3870                                                               _("Fidelity"), _("Fidelity:"),
3871                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3872                                                               "/tools/tweak/fidelity", 50,
3873                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3874                                                               1, 100, 1.0, 10.0,
3875                                                               labels, values, G_N_ELEMENTS(labels),
3876                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3877         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3878         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3879         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3880             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3881         g_object_set_data( holder, "tweak_fidelity", eact );
3882     }
3885     /* Use Pressure button */
3886     {
3887         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3888                                                       _("Pressure"),
3889                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3890                                                       "use_pressure",
3891                                                       Inkscape::ICON_SIZE_DECORATION );
3892         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3893         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3894         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
3895     }
3900 //########################
3901 //##     Calligraphy    ##
3902 //########################
3903 static void update_presets_list (GObject *tbl)
3905     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3906     if (g_object_get_data(tbl, "presets_blocked"))
3907         return;
3909     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3910     if (!sel) {
3911         // WTF!? This will cause a segfault if ever reached
3912         //ege_select_one_action_set_active(sel, 0);
3913         return;
3914     }
3916     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
3918     int ege_index = 1;
3919     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
3920         bool match = true;
3921         
3922         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
3923         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
3924             Glib::ustring entry_name = j->getEntryName();
3925             if (entry_name == "id" || entry_name == "name") continue;
3927             void *widget = g_object_get_data(tbl, entry_name.data());
3928             if (widget) {
3929                 if (GTK_IS_ADJUSTMENT(widget)) {
3930                     double v = j->getDouble();
3931                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3932                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3933                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3934                         match = false;
3935                         break;
3936                     }
3937                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3938                     bool v = j->getBool();
3939                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3940                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3941                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
3942                         match = false;
3943                         break;
3944                     }
3945                 }
3946             }
3947         }
3948         
3949         if (match) {
3950             // newly added item is at the same index as the
3951             // save command, so we need to change twice for it to take effect
3952             ege_select_one_action_set_active(sel, 0);
3953             ege_select_one_action_set_active(sel, ege_index); // one-based index
3954             return;
3955         }
3956     }
3958     // no match found
3959     ege_select_one_action_set_active(sel, 0);
3962 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3964     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3965     prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
3966     update_presets_list(tbl);
3969 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3971     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3972     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
3973     update_presets_list(tbl);
3976 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3978     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3979     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
3980     update_presets_list(tbl);
3983 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3985     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3986     prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
3987     update_presets_list(tbl);
3990 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3992     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3993     prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
3994     update_presets_list(tbl);
3997 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3999     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4000     prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4001     update_presets_list(tbl);
4004 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4006     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4007     prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4008     update_presets_list(tbl);
4011 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4013     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4014     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4015     update_presets_list(tbl);
4018 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4020     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4021     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4022     update_presets_list(tbl);
4025 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4027     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4028     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4029     update_presets_list(tbl);
4032 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4034     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4035     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4036     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4037     update_presets_list(tbl);
4038     if (calligraphy_angle )
4039         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4043 static gchar const *const widget_names[] = {
4044     "width",
4045     "mass",
4046     "wiggle",
4047     "angle",
4048     "thinning",
4049     "tremor",
4050     "flatness",
4051     "cap_rounding",
4052     "usepressure",
4053     "tracebackground",
4054     "usetilt"
4055 };
4058 static void sp_dcc_build_presets_list(GObject *tbl)
4060     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4062     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4063     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4064     gtk_list_store_clear (model);
4066     {
4067         GtkTreeIter iter;
4068         gtk_list_store_append( model, &iter );
4069         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4070     }
4071     
4072     // iterate over all presets to populate the list
4073     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4075     int ii=1;
4076     
4077     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4078         GtkTreeIter iter;
4079         Glib::ustring preset_name = prefs->getString(*i + "/name");
4080         gtk_list_store_append( model, &iter );
4081         gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4082     }
4084     {
4085         GtkTreeIter iter;
4086         gtk_list_store_append( model, &iter );
4087         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4088         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4089     }
4091     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4093     update_presets_list (tbl);
4096 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4098     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4099     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4100     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4101     if (! desktop) return;
4103     if (g_object_get_data(tbl, "presets_blocked"))
4104         return;
4106     CalligraphicProfileRename::show(desktop);
4107     if ( !CalligraphicProfileRename::applied()) {
4108         // dialog cancelled
4109         update_presets_list (tbl);
4110         return;
4111     }
4112     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4114     if (profile_name.empty()) {
4115         // empty name entered
4116         update_presets_list (tbl);
4117         return;
4118     }
4120     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4121     
4122     // If there's a preset with the given name, find it and set save_path appropriately
4123     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4124     int total_presets = presets.size();
4125     int new_index = -1;
4126     Glib::ustring save_path; // profile pref path without a trailing slash
4127     
4128     int temp_index = 0;
4129     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4130         Glib::ustring name = prefs->getString(*i + "/name");
4131         if (!name.empty() && profile_name == name) {
4132             new_index = temp_index;
4133             save_path = *i;
4134             break;
4135         }
4136     }
4138     if (new_index == -1) {
4139         // no preset with this name, create
4140         new_index = total_presets + 1;
4141         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4142         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4143         g_free(profile_id);
4144     }
4146     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4147         gchar const *const widget_name = widget_names[i];
4148         void *widget = g_object_get_data(tbl, widget_name);
4149         if (widget) {
4150             if (GTK_IS_ADJUSTMENT(widget)) {
4151                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4152                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4153                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4154             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4155                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4156                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4157                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4158             } else {
4159                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4160             }
4161         } else {
4162             g_warning("Bad key when writing preset: %s\n", widget_name);
4163         }
4164     }
4165     prefs->setString(save_path + "/name", profile_name);
4167     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4168     sp_dcc_build_presets_list (tbl);
4172 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4174     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4176     gint preset_index = ege_select_one_action_get_active( act );
4177     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4178     // even when the preset is not changed. It would be good to replace it with something more
4179     // modern. Index 0 means "No preset", so we don't do anything.
4180     if (preset_index == 0) return;
4182     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4184     if (preset_index == save_presets_index) {
4185         // this is the Save command
4186         sp_dcc_save_profile(NULL, tbl);
4187         return;
4188     }
4190     if (g_object_get_data(tbl, "presets_blocked"))
4191         return;
4193     // preset_index is one-based so we subtract 1
4194     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4195     Glib::ustring preset_path = presets.at(preset_index - 1);
4197     if (!preset_path.empty()) {
4198         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
4199         
4200         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4202         // Shouldn't this be std::map?
4203         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4204             Glib::ustring entry_name = i->getEntryName();
4205             if (entry_name == "id" || entry_name == "name") continue;
4206             void *widget = g_object_get_data(tbl, entry_name.data());
4207             if (widget) {
4208                 if (GTK_IS_ADJUSTMENT(widget)) {
4209                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4210                     gtk_adjustment_set_value(adj, i->getDouble());
4211                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4212                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4213                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4214                     gtk_toggle_action_set_active(toggle, i->getBool());
4215                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4216                 } else {
4217                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4218                 }
4219             } else {
4220                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4221             }
4222         }
4223         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4224     }
4228 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4230     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4231     {
4232         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4234         EgeAdjustmentAction* calligraphy_angle = 0;
4236         {
4237         /* Width */
4238         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4239         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4240         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4241                                                               _("Pen Width"), _("Width:"),
4242                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4243                                                               "/tools/calligraphic/width", 15,
4244                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4245                                                               1, 100, 1.0, 0.0,
4246                                                               labels, values, G_N_ELEMENTS(labels),
4247                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4248         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4249         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4250         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4251         }
4253         {
4254         /* Thinning */
4255             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4256             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4257         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4258                                                               _("Stroke Thinning"), _("Thinning:"),
4259                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4260                                                               "/tools/calligraphic/thinning", 10,
4261                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4262                                                               -100, 100, 1, 0.1,
4263                                                               labels, values, G_N_ELEMENTS(labels),
4264                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4265         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4266         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4267         }
4269         {
4270         /* Angle */
4271         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4272         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4273         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4274                                                               _("Pen Angle"), _("Angle:"),
4275                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4276                                                               "/tools/calligraphic/angle", 30,
4277                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4278                                                               -90.0, 90.0, 1.0, 10.0,
4279                                                               labels, values, G_N_ELEMENTS(labels),
4280                                                               sp_ddc_angle_value_changed, 1, 0 );
4281         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4282         g_object_set_data( holder, "angle_action", eact );
4283         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4284         calligraphy_angle = eact;
4285         }
4287         {
4288         /* Fixation */
4289             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4290         gdouble values[] = {0, 20, 40, 60, 90, 100};
4291         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4292                                                               _("Fixation"), _("Fixation:"),
4293                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4294                                                               "/tools/calligraphic/flatness", 90,
4295                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4296                                                               0.0, 100, 1.0, 10.0,
4297                                                               labels, values, G_N_ELEMENTS(labels),
4298                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4299         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4301         }
4303         {
4304         /* Cap Rounding */
4305             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4306         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4307         // TRANSLATORS: "cap" means "end" (both start and finish) here
4308         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4309                                                               _("Cap rounding"), _("Caps:"),
4310                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4311                                                               "/tools/calligraphic/cap_rounding", 0.0,
4312                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4313                                                               0.0, 5.0, 0.01, 0.1,
4314                                                               labels, values, G_N_ELEMENTS(labels),
4315                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4316         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4317         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4318         }
4320         {
4321         /* Tremor */
4322             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4323         gdouble values[] = {0, 10, 20, 40, 60, 100};
4324         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4325                                                               _("Stroke Tremor"), _("Tremor:"),
4326                                                               _("Increase to make strokes rugged and trembling"),
4327                                                               "/tools/calligraphic/tremor", 0.0,
4328                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4329                                                               0.0, 100, 1, 0.0,
4330                                                               labels, values, G_N_ELEMENTS(labels),
4331                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4333         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4334         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4335         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4336         }
4338         {
4339         /* Wiggle */
4340         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4341         gdouble values[] = {0, 20, 40, 60, 100};
4342         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4343                                                               _("Pen Wiggle"), _("Wiggle:"),
4344                                                               _("Increase to make the pen waver and wiggle"),
4345                                                               "/tools/calligraphic/wiggle", 0.0,
4346                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4347                                                               0.0, 100, 1, 0.0,
4348                                                               labels, values, G_N_ELEMENTS(labels),
4349                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4350         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4351         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4352         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4353         }
4355         {
4356         /* Mass */
4357             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4358         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4359         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4360                                                               _("Pen Mass"), _("Mass:"),
4361                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4362                                                               "/tools/calligraphic/mass", 2.0,
4363                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4364                                                               0.0, 100, 1, 0.0,
4365                                                               labels, values, G_N_ELEMENTS(labels),
4366                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4367         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4368         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4369         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4370         }
4373         /* Trace Background button */
4374         {
4375             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4376                                                           _("Trace Background"),
4377                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4378                                                           "trace_background",
4379                                                           Inkscape::ICON_SIZE_DECORATION );
4380             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4381             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4382             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4383             g_object_set_data( holder, "tracebackground", act );
4384         }
4386         /* Use Pressure button */
4387         {
4388             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4389                                                           _("Pressure"),
4390                                                           _("Use the pressure of the input device to alter the width of the pen"),
4391                                                           "use_pressure",
4392                                                           Inkscape::ICON_SIZE_DECORATION );
4393             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4394             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4395             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4396             g_object_set_data( holder, "usepressure", act );
4397         }
4399         /* Use Tilt button */
4400         {
4401             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4402                                                           _("Tilt"),
4403                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4404                                                           "use_tilt",
4405                                                           Inkscape::ICON_SIZE_DECORATION );
4406             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4407             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4408             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4409             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4410             g_object_set_data( holder, "usetilt", act );
4411         }
4413         /*calligraphic profile */
4414         {
4415             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4416             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4417             ege_select_one_action_set_appearance (act1, "compact");
4418             g_object_set_data (holder, "profile_selector", act1 );
4420             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4422             sp_dcc_build_presets_list (holder);
4424             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4425             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4426         }
4427     }
4431 //########################
4432 //##    Circle / Arc    ##
4433 //########################
4435 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4437     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4438     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4440     if (v1 == 0 && v2 == 0) {
4441         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4442             gtk_action_set_sensitive( ocb, FALSE );
4443             gtk_action_set_sensitive( make_whole, FALSE );
4444         }
4445     } else {
4446         gtk_action_set_sensitive( ocb, TRUE );
4447         gtk_action_set_sensitive( make_whole, TRUE );
4448     }
4451 static void
4452 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4454     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4456     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4457         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458         prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4459     }
4461     // quit if run by the attr_changed listener
4462     if (g_object_get_data( tbl, "freeze" )) {
4463         return;
4464     }
4466     // in turn, prevent listener from responding
4467     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4469     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4471     bool modmade = false;
4472     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4473          items != NULL;
4474          items = items->next)
4475     {
4476         SPItem *item = SP_ITEM(items->data);
4478         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4480             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4481             SPArc *arc = SP_ARC(item);
4483             if (!strcmp(value_name, "start"))
4484                 ge->start = (adj->value * M_PI)/ 180;
4485             else
4486                 ge->end = (adj->value * M_PI)/ 180;
4488             sp_genericellipse_normalize(ge);
4489             ((SPObject *)arc)->updateRepr();
4490             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4492             modmade = true;
4493         }
4494     }
4496     g_free(namespaced_name);
4498     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4500     sp_arctb_sensitivize( tbl, adj->value, other->value );
4502     if (modmade) {
4503         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4504                                    _("Arc: Change start/end"));
4505     }
4507     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4511 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4513     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4516 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4518     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4522 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4524     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4525     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4526         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4527         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4528     }
4530     // quit if run by the attr_changed listener
4531     if (g_object_get_data( tbl, "freeze" )) {
4532         return;
4533     }
4535     // in turn, prevent listener from responding
4536     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4538     bool modmade = false;
4540     if ( ege_select_one_action_get_active(act) != 0 ) {
4541         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4542              items != NULL;
4543              items = items->next)
4544         {
4545             if (SP_IS_ARC((SPItem *) items->data)) {
4546                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4547                 repr->setAttribute("sodipodi:open", "true");
4548                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4549                 modmade = true;
4550             }
4551         }
4552     } else {
4553         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4554              items != NULL;
4555              items = items->next)
4556         {
4557             if (SP_IS_ARC((SPItem *) items->data))    {
4558                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4559                 repr->setAttribute("sodipodi:open", NULL);
4560                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4561                 modmade = true;
4562             }
4563         }
4564     }
4566     if (modmade) {
4567         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4568                                    _("Arc: Change open/closed"));
4569     }
4571     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4574 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4576     GtkAdjustment *adj;
4577     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4578     gtk_adjustment_set_value(adj, 0.0);
4579     gtk_adjustment_value_changed(adj);
4581     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4582     gtk_adjustment_set_value(adj, 0.0);
4583     gtk_adjustment_value_changed(adj);
4585     spinbutton_defocus( GTK_OBJECT(obj) );
4588 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4589                                       gchar const */*old_value*/, gchar const */*new_value*/,
4590                                       bool /*is_interactive*/, gpointer data)
4592     GObject *tbl = G_OBJECT(data);
4594     // quit if run by the _changed callbacks
4595     if (g_object_get_data( tbl, "freeze" )) {
4596         return;
4597     }
4599     // in turn, prevent callbacks from responding
4600     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4602     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4603     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4605     GtkAdjustment *adj1,*adj2;
4606     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4607     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4608     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4609     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4611     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4613     char const *openstr = NULL;
4614     openstr = repr->attribute("sodipodi:open");
4615     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4617     if (openstr) {
4618         ege_select_one_action_set_active( ocb, 1 );
4619     } else {
4620         ege_select_one_action_set_active( ocb, 0 );
4621     }
4623     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4626 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4627     NULL, /* child_added */
4628     NULL, /* child_removed */
4629     arc_tb_event_attr_changed,
4630     NULL, /* content_changed */
4631     NULL  /* order_changed */
4632 };
4635 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4637     int n_selected = 0;
4638     Inkscape::XML::Node *repr = NULL;
4640     purge_repr_listener( tbl, tbl );
4642     for (GSList const *items = selection->itemList();
4643          items != NULL;
4644          items = items->next)
4645     {
4646         if (SP_IS_ARC((SPItem *) items->data)) {
4647             n_selected++;
4648             repr = SP_OBJECT_REPR((SPItem *) items->data);
4649         }
4650     }
4652     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4654     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4655     if (n_selected == 0) {
4656         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4657     } else if (n_selected == 1) {
4658         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4659         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4661         if (repr) {
4662             g_object_set_data( tbl, "repr", repr );
4663             Inkscape::GC::anchor(repr);
4664             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4665             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4666         }
4667     } else {
4668         // FIXME: implement averaging of all parameters for multiple selected
4669         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4670         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4671         sp_arctb_sensitivize( tbl, 1, 0 );
4672     }
4676 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4678     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4680     EgeAdjustmentAction* eact = 0;
4681     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4684     {
4685         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4686         ege_output_action_set_use_markup( act, TRUE );
4687         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4688         g_object_set_data( holder, "mode_action", act );
4689     }
4691     /* Start */
4692     {
4693         eact = create_adjustment_action( "ArcStartAction",
4694                                          _("Start"), _("Start:"),
4695                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4696                                          "/tools/shapes/arc/start", 0.0,
4697                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4698                                          -360.0, 360.0, 1.0, 10.0,
4699                                          0, 0, 0,
4700                                          sp_arctb_start_value_changed);
4701         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4702     }
4704     /* End */
4705     {
4706         eact = create_adjustment_action( "ArcEndAction",
4707                                          _("End"), _("End:"),
4708                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4709                                          "/tools/shapes/arc/end", 0.0,
4710                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4711                                          -360.0, 360.0, 1.0, 10.0,
4712                                          0, 0, 0,
4713                                          sp_arctb_end_value_changed);
4714         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4715     }
4717     /* Segments / Pie checkbox */
4718     {
4719         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4721         GtkTreeIter iter;
4722         gtk_list_store_append( model, &iter );
4723         gtk_list_store_set( model, &iter,
4724                             0, _("Closed arc"),
4725                             1, _("Switch to segment (closed shape with two radii)"),
4726                             2, "circle_closed_arc",
4727                             -1 );
4729         gtk_list_store_append( model, &iter );
4730         gtk_list_store_set( model, &iter,
4731                             0, _("Open Arc"),
4732                             1, _("Switch to arc (unclosed shape)"),
4733                             2, "circle_open_arc",
4734                             -1 );
4736         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4737         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4738         g_object_set_data( holder, "open_action", act );
4740         ege_select_one_action_set_appearance( act, "full" );
4741         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4742         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4743         ege_select_one_action_set_icon_column( act, 2 );
4744         ege_select_one_action_set_icon_size( act, secondarySize );
4745         ege_select_one_action_set_tooltip_column( act, 1  );
4747         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
4748         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4749         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4750     }
4752     /* Make Whole */
4753     {
4754         InkAction* inky = ink_action_new( "ArcResetAction",
4755                                           _("Make whole"),
4756                                           _("Make the shape a whole ellipse, not arc or segment"),
4757                                           "reset_circle",
4758                                           secondarySize );
4759         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4760         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4761         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4762         g_object_set_data( holder, "make_whole", inky );
4763     }
4765     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4766     // sensitivize make whole and open checkbox
4767     {
4768         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4769         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4770         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4771     }
4774     sigc::connection *connection = new sigc::connection(
4775         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4776         );
4777     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4778     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4784 // toggle button callbacks and updaters
4786 //########################
4787 //##      Dropper       ##
4788 //########################
4790 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4791     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4792     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
4793     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4794     if ( set_action ) {
4795         if ( gtk_toggle_action_get_active( act ) ) {
4796             gtk_action_set_sensitive( set_action, TRUE );
4797         } else {
4798             gtk_action_set_sensitive( set_action, FALSE );
4799         }
4800     }
4802     spinbutton_defocus(GTK_OBJECT(tbl));
4805 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4806     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4807     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
4808     spinbutton_defocus(GTK_OBJECT(tbl));
4812 /**
4813  * Dropper auxiliary toolbar construction and setup.
4814  *
4815  * TODO: Would like to add swatch of current color.
4816  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4817  *       can drag and drop places. Will provide a nice mixing palette.
4818  */
4819 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4821     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4822     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
4824     {
4825         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4826         ege_output_action_set_use_markup( act, TRUE );
4827         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4828     }
4830     {
4831         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4832                                                       _("Pick opacity"),
4833                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4834                                                       NULL,
4835                                                       Inkscape::ICON_SIZE_DECORATION );
4836         g_object_set( act, "short_label", _("Pick"), NULL );
4837         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4838         g_object_set_data( holder, "pick_action", act );
4839         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4840         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4841     }
4843     {
4844         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4845                                                       _("Assign opacity"),
4846                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4847                                                       NULL,
4848                                                       Inkscape::ICON_SIZE_DECORATION );
4849         g_object_set( act, "short_label", _("Assign"), NULL );
4850         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4851         g_object_set_data( holder, "set_action", act );
4852         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
4853         // make sure it's disabled if we're not picking alpha
4854         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4855         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4856     }
4860 //########################
4861 //##      LPETool       ##
4862 //########################
4864 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4866 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4867 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4869     using namespace Inkscape::LivePathEffect;
4871     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4872     SPEventContext *ec = desktop->event_context;
4873     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4874         return;
4875     }
4877     // only take action if run by the attr_changed listener
4878     if (!g_object_get_data(tbl, "freeze")) {
4879         // in turn, prevent listener from responding
4880         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4882         gint mode = ege_select_one_action_get_active(act);
4883         EffectType type = lpesubtools[mode];
4885         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4886         bool success = lpetool_try_construction(lc, type);
4887         if (success) {
4888             // since the construction was already performed, we set the state back to inactive
4889             ege_select_one_action_set_active(act, 0);
4890             mode = 0;
4891         } else {
4892             // switch to the chosen subtool
4893             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4894         }
4896         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4897             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4898             prefs->setInt( "/tools/lpetool/mode", mode );
4899         }
4901         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4902     }
4905 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
4907     SPEventContext *ec = selection->desktop()->event_context;
4908     if (!SP_IS_LPETOOL_CONTEXT(ec))
4909         return;
4911     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4914 void
4915 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4917     using namespace Inkscape::LivePathEffect;
4918     SPEventContext *ec = selection->desktop()->event_context;
4919     if (!SP_IS_LPETOOL_CONTEXT(ec))
4920         return;
4921     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4923     lpetool_delete_measuring_items(lc);
4924     lpetool_create_measuring_items(lc, selection);
4926     // activate line segment combo box if a single item with LPELineSegment is selected
4927     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4928     SPItem *item = selection->singleItem();
4929     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4930         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4931         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4932         if (lpe && lpe->effectType() == LINE_SEGMENT) {
4933             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4934             g_object_set_data(tbl, "currentlpe", lpe);
4935             g_object_set_data(tbl, "currentlpeitem", lpeitem);
4936             gtk_action_set_sensitive(w, TRUE);
4937             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4938         } else {
4939             g_object_set_data(tbl, "currentlpe", NULL);
4940             g_object_set_data(tbl, "currentlpeitem", NULL);
4941             gtk_action_set_sensitive(w, FALSE);
4942         }
4943     } else {
4944         g_object_set_data(tbl, "currentlpe", NULL);
4945         g_object_set_data(tbl, "currentlpeitem", NULL);
4946         gtk_action_set_sensitive(w, FALSE);
4947     }
4950 static void
4951 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4952     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4953     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4955     bool show = gtk_toggle_action_get_active( act );
4956     prefs->setBool("/tools/lpetool/show_bbox",  show);
4958     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4959         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4960         lpetool_context_reset_limiting_bbox(lc);
4961     }
4964 static void
4965 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4966     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4967     if (!tools_isactive(desktop, TOOLS_LPETOOL))
4968         return;
4970     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4971     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4972     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4973         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4974         bool show = gtk_toggle_action_get_active( act );
4975         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
4976         lpetool_show_measuring_info(lc, show);
4977         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4978     }
4981 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
4982     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4983     SPUnit const *unit = tracker->getActiveUnit();
4984     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4985     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
4987     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4988     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4989         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4990         lpetool_delete_measuring_items(lc);
4991         lpetool_create_measuring_items(lc);
4992     }
4995 static void
4996 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4997     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4998     Inkscape::Selection *selection = desktop->selection;
5000     Geom::OptRect bbox = selection->bounds();
5002     if (bbox) {
5003         Geom::Point A(bbox->min());
5004         Geom::Point B(bbox->max());
5006         A *= desktop->doc2dt();
5007         B *= desktop->doc2dt();
5009         // TODO: should we provide a way to store points in prefs?
5010         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5011         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5012         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5013         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5014         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5016         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5017     }
5019     gtk_toggle_action_set_active(act, false);
5022 static void
5023 sp_line_segment_build_list(GObject *tbl)
5025     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5027     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5028     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5029     gtk_list_store_clear (model);
5031     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5032     {
5033         GtkTreeIter iter;
5034         gtk_list_store_append( model, &iter );
5035         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5036         gtk_list_store_append( model, &iter );
5037         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5038         gtk_list_store_append( model, &iter );
5039         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5040         gtk_list_store_append( model, &iter );
5041         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5042     }
5044     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5047 static void
5048 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5049     using namespace Inkscape::LivePathEffect;
5051     // quit if run by the attr_changed listener
5052     if (g_object_get_data(tbl, "freeze")) {
5053         return;
5054     }
5056     // in turn, prevent listener from responding
5057     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5059     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5060     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5061     if (lpeitem) {
5062         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5063         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5064         sp_lpe_item_update_patheffect(lpeitem, true, true);
5065     }
5067     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5070 static void
5071 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5072     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5074     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5075         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5076     }
5077     gtk_toggle_action_set_active(act, false);
5080 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5082     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5083     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5084     g_object_set_data(holder, "tracker", tracker);
5085     SPUnit const *unit = tracker->getActiveUnit();
5087     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5088     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5090     /** Automatically create a list of LPEs that get added to the toolbar **/
5091     {
5092         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5094         GtkTreeIter iter;
5096         // the first toggle button represents the state that no subtool is active (remove this when
5097         // this can be modeled by EgeSelectOneAction or some other action)
5098         gtk_list_store_append( model, &iter );
5099         gtk_list_store_set( model, &iter,
5100                             0, _("All inactive"),
5101                             1, _("No geometric tool is active"),
5102                             2, _("all_inactive"),
5103                             -1 );
5105         Inkscape::LivePathEffect::EffectType type;
5106         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5107             type =  lpesubtools[i];
5108             gtk_list_store_append( model, &iter );
5109             gtk_list_store_set( model, &iter,
5110                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5111                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5112                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5113                                 -1 );
5114         }
5116         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5117         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5118         g_object_set_data( holder, "lpetool_mode_action", act );
5120         ege_select_one_action_set_appearance( act, "full" );
5121         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5122         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5123         ege_select_one_action_set_icon_column( act, 2 );
5124         ege_select_one_action_set_tooltip_column( act, 1  );
5126         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5127         ege_select_one_action_set_active( act, lpeToolMode );
5128         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5129     }
5131     /* Show limiting bounding box */
5132     {
5133         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5134                                                       _("Show limiting bounding box"),
5135                                                       _("Show bounding box (used to cut infinite lines)"),
5136                                                       "lpetool_show_bbox",
5137                                                       Inkscape::ICON_SIZE_DECORATION );
5138         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5139         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5140         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5141     }
5143     /* Set limiting bounding box to bbox of current selection */
5144     {
5145         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5146                                                       _("Get limiting bounding box from selection"),
5147                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5148                                                       "lpetool_set_bbox",
5149                                                       Inkscape::ICON_SIZE_DECORATION );
5150         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5151         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5152         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5153     }
5156     /* Combo box to choose line segment type */
5157     {
5158         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5159         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5160         ege_select_one_action_set_appearance (act, "compact");
5161         g_object_set_data (holder, "lpetool_line_segment_action", act );
5163         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5165         sp_line_segment_build_list (holder);
5167         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5168         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5169         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5170     }
5172     /* Display measuring info for selected items */
5173     {
5174         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5175                                                       _("Display measuring info"),
5176                                                       _("Display measuring info for selected items"),
5177                                                       "lpetool_measuring_info",
5178                                                       Inkscape::ICON_SIZE_DECORATION );
5179         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5180         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5181         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5182     }
5184     // add the units menu
5185     {
5186         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5187         gtk_action_group_add_action( mainActions, act );
5188         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5189         g_object_set_data(holder, "lpetool_units_action", act);
5190         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5191     }
5193     /* Open LPE dialog (to adapt parameters numerically) */
5194     {
5195         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5196                                                       _("Open LPE dialog"),
5197                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5198                                                       "lpetool_open_lpe_dialog",
5199                                                       Inkscape::ICON_SIZE_DECORATION );
5200         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5201         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5202         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5203     }
5205     //watch selection
5206     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5208     sigc::connection *c_selection_modified =
5209         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5210                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5211     pool->add_connection ("selection-modified", c_selection_modified);
5213     sigc::connection *c_selection_changed =
5214         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5215                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5216     pool->add_connection ("selection-changed", c_selection_changed);
5219 //########################
5220 //##       Eraser       ##
5221 //########################
5223 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5225     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5226     prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5227     update_presets_list(tbl);
5230 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5232     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5233     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5234     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5235         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5236         prefs->setBool( "/tools/eraser/mode", eraserMode );
5237     }
5239     // only take action if run by the attr_changed listener
5240     if (!g_object_get_data( tbl, "freeze" )) {
5241         // in turn, prevent listener from responding
5242         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5244         if ( eraserMode != 0 ) {
5245         } else {
5246         }
5247         // TODO finish implementation
5249         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5250     }
5253 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5255     {
5256         /* Width */
5257         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5258         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5259         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5260                                                               _("Pen Width"), _("Width:"),
5261                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5262                                                               "/tools/eraser/width", 15,
5263                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5264                                                               1, 100, 1.0, 0.0,
5265                                                               labels, values, G_N_ELEMENTS(labels),
5266                                                               sp_erc_width_value_changed,  0.01, 0, 100 );
5267         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5269         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5270     }
5272     {
5273         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5275         GtkTreeIter iter;
5276         gtk_list_store_append( model, &iter );
5277         gtk_list_store_set( model, &iter,
5278                             0, _("Delete"),
5279                             1, _("Delete objects touched by the eraser"),
5280                             2, "delete_object",
5281                             -1 );
5283         gtk_list_store_append( model, &iter );
5284         gtk_list_store_set( model, &iter,
5285                             0, _("Cut"),
5286                             1, _("Cut out from objects"),
5287                             2, "difference",
5288                             -1 );
5290         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5291         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5292         g_object_set_data( holder, "eraser_mode_action", act );
5294         ege_select_one_action_set_appearance( act, "full" );
5295         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5296         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5297         ege_select_one_action_set_icon_column( act, 2 );
5298         ege_select_one_action_set_tooltip_column( act, 1  );
5300         /// @todo Convert to boolean?
5301         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5302         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5303         ege_select_one_action_set_active( act, eraserMode );
5304         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5305     }
5309 //########################
5310 //##    Text Toolbox    ##
5311 //########################
5312 /*
5313 static void
5314 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5316     //Call back for letter sizing spinbutton
5319 static void
5320 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5322     //Call back for line height spinbutton
5325 static void
5326 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5328     //Call back for horizontal kerning spinbutton
5331 static void
5332 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5334     //Call back for vertical kerning spinbutton
5337 static void
5338 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5340     //Call back for letter rotation spinbutton
5341 }*/
5343 namespace {
5345 bool popdown_visible = false;
5346 bool popdown_hasfocus = false;
5348 void
5349 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5351     SPStyle *query =
5352         sp_style_new (SP_ACTIVE_DOCUMENT);
5354 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5356     int result_family =
5357         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5359     int result_style =
5360         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5362     int result_numbers =
5363         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5365     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5367     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5368     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5369         // there are no texts in selection, read from prefs
5370         
5371             sp_style_read_from_prefs(query, "/tools/text");
5372             
5373             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5374             // do not reset the toolbar style from prefs if we already did it last time
5375             sp_style_unref(query);
5376             return;
5377         }
5378         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5379     } else {
5380         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5381     }
5383     if (query->text)
5384     {
5385         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5386             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5387             gtk_entry_set_text (GTK_ENTRY (entry), "");
5389         } else if (query->text->font_specification.value || query->text->font_family.value) {
5391             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5393             // Get the font that corresponds
5394             Glib::ustring familyName;
5396             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5397             if (font) {
5398                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5399                 font->Unref();
5400                 font = NULL;
5401             }
5403             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5405             Gtk::TreePath path;
5406             try {
5407                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5408             } catch (...) {
5409                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5410                 sp_style_unref(query);
5411                 return;
5412             }
5414             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5415             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5417             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5419             gtk_tree_selection_select_path (tselection, path.gobj());
5420             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5422             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5423         }
5425         //Size
5426         {
5427             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5428             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5429             g_object_set_data(tbl, "size-block", gpointer(1));
5430             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5431             g_object_set_data(tbl, "size-block", gpointer(0));
5432             g_free(str);
5433         }
5435         //Anchor
5436         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5437         {
5438             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5439             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5440             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5441             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5442         }
5443         else
5444         {
5445             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5446             {
5447                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5448                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5449                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5450                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5451             }
5452             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5453             {
5454                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5455                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5456                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5457                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5458             }
5459             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5460             {
5461                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5462                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5463                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5464                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5465             }
5466         }
5468         //Style
5469         {
5470             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5472             gboolean active = gtk_toggle_button_get_active (button);
5473             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5475             if (active != check)
5476             {
5477                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5478                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5479                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5480             }
5481         }
5483         {
5484             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5486             gboolean active = gtk_toggle_button_get_active (button);
5487             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5489             if (active != check)
5490             {
5491                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5492                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5493                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5494             }
5495         }
5497         //Orientation
5498         //locking both buttons, changing one affect all group (both)
5499         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5500         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5502         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5503         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5505         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5506         {
5507             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5508         }
5509         else
5510         {
5511             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5512         }
5513         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5514         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5515     }
5517     sp_style_unref(query);
5520 void
5521 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5523     sp_text_toolbox_selection_changed (selection, tbl);
5526 void
5527 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5529     sp_text_toolbox_selection_changed (NULL, tbl);
5532 void
5533 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5534                                 GObject             *tbl)
5536     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5537     GtkTreeModel *model = 0;
5538     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5539     GtkTreeIter   iter;
5540     char         *family = 0;
5542     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5543     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5545     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5546         return;
5547     }
5549     gtk_tree_model_get (model, &iter, 0, &family, -1);
5551     if (g_object_get_data (G_OBJECT (selection), "block"))
5552     {
5553         gtk_entry_set_text (GTK_ENTRY (entry), family);
5554         return;
5555     }
5557     gtk_entry_set_text (GTK_ENTRY (entry), family);
5559     SPStyle *query =
5560         sp_style_new (SP_ACTIVE_DOCUMENT);
5562     int result_fontspec =
5563         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5565     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5567     SPCSSAttr *css = sp_repr_css_attr_new ();
5570     // First try to get the font spec from the stored value
5571     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5573     if (fontSpec.empty()) {
5574         // Construct a new font specification if it does not yet exist
5575         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5576         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5577         fontFromStyle->Unref();
5578     }
5580     if (!fontSpec.empty()) {
5581         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5582         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5583             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5584             if (font) {
5585                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5587                 // Set all the these just in case they were altered when finding the best
5588                 // match for the new family and old style...
5590                 gchar c[256];
5592                 font->Family(c, 256);
5593                 sp_repr_css_set_property (css, "font-family", c);
5595                 font->Attribute( "weight", c, 256);
5596                 sp_repr_css_set_property (css, "font-weight", c);
5598                 font->Attribute("style", c, 256);
5599                 sp_repr_css_set_property (css, "font-style", c);
5601                 font->Attribute("stretch", c, 256);
5602                 sp_repr_css_set_property (css, "font-stretch", c);
5604                 font->Attribute("variant", c, 256);
5605                 sp_repr_css_set_property (css, "font-variant", c);
5607                 font->Unref();
5608             }
5609         }
5610     }
5612     // If querying returned nothing, set the default style of the tool (for new texts)
5613     if (result_fontspec == QUERY_STYLE_NOTHING)
5614     {
5615         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5616         prefs->setStyle("/tools/text/style", css);
5617         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5618     }
5619     else
5620     {
5621         sp_desktop_set_style (desktop, css, true, true);
5622     }
5624     sp_style_unref(query);
5626     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5627                                    _("Text: Change font family"));
5628     sp_repr_css_attr_unref (css);
5629     g_free(family);
5630     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5632     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5635 /* This is where execution comes when the contents of the font family box have been completed
5636    by the press of the return key */
5637 void
5638 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5639                                        GObject      *tbl)
5641     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5643 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5644     try {
5645         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5646         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5647         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5648         gtk_tree_selection_select_path (selection, path.gobj());
5649         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5650         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5651     } catch (...) {
5652         if (family && strlen (family))
5653         {
5654             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5655         }
5656     }
5659 void
5660 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5661                                    gpointer          data)
5663     if (g_object_get_data (G_OBJECT (button), "block")) return;
5664     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5665     int prop = GPOINTER_TO_INT(data);
5667     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5668     SPCSSAttr *css = sp_repr_css_attr_new ();
5670     switch (prop)
5671     {
5672         case 0:
5673         {
5674             sp_repr_css_set_property (css, "text-anchor", "start");
5675             sp_repr_css_set_property (css, "text-align", "start");
5676             break;
5677         }
5678         case 1:
5679         {
5680             sp_repr_css_set_property (css, "text-anchor", "middle");
5681             sp_repr_css_set_property (css, "text-align", "center");
5682             break;
5683         }
5685         case 2:
5686         {
5687             sp_repr_css_set_property (css, "text-anchor", "end");
5688             sp_repr_css_set_property (css, "text-align", "end");
5689             break;
5690         }
5692         case 3:
5693         {
5694             sp_repr_css_set_property (css, "text-anchor", "start");
5695             sp_repr_css_set_property (css, "text-align", "justify");
5696             break;
5697         }
5698     }
5700     SPStyle *query =
5701         sp_style_new (SP_ACTIVE_DOCUMENT);
5702     int result_numbers =
5703         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5705     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5706     if (result_numbers == QUERY_STYLE_NOTHING)
5707     {
5708         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5709         prefs->setStyle("/tools/text/style", css);
5710     }
5712     sp_style_unref(query);
5714     sp_desktop_set_style (desktop, css, true, true);
5715     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5716                                    _("Text: Change alignment"));
5717     sp_repr_css_attr_unref (css);
5719     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5722 void
5723 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5724                                gpointer          data)
5726     if (g_object_get_data (G_OBJECT (button), "block")) return;
5728     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5729     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5730     int          prop       = GPOINTER_TO_INT(data);
5731     bool         active     = gtk_toggle_button_get_active (button);
5733     SPStyle *query =
5734         sp_style_new (SP_ACTIVE_DOCUMENT);
5736     int result_fontspec =
5737         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5739     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5740     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5741     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5743     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5744     Glib::ustring newFontSpec = "";
5746     if (fontSpec.empty()) {
5747         // Construct a new font specification if it does not yet exist
5748         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5749         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5750         fontFromStyle->Unref();
5751     }
5753     switch (prop)
5754     {
5755         case 0:
5756         {
5757             if (!fontSpec.empty()) {
5758                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5759             }
5760             if (fontSpec != newFontSpec) {
5761                 // Don't even set the bold if the font didn't exist on the system
5762                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5763             }
5764             break;
5765         }
5767         case 1:
5768         {
5769             if (!fontSpec.empty()) {
5770                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5771             }
5772             if (fontSpec != newFontSpec) {
5773                 // Don't even set the italic if the font didn't exist on the system
5774                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5775             }
5776             break;
5777         }
5778     }
5780     if (!newFontSpec.empty()) {
5781         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5782     }
5784     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5785     if (result_fontspec == QUERY_STYLE_NOTHING)
5786     {
5787         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5788         prefs->setStyle("/tools/text/style", css);
5789     }
5791     sp_style_unref(query);
5793     sp_desktop_set_style (desktop, css, true, true);
5794     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5795                                    _("Text: Change font style"));
5796     sp_repr_css_attr_unref (css);
5798     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5801 void
5802 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5803                                      gpointer         data)
5805     if (g_object_get_data (G_OBJECT (button), "block")) {
5806         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5807         return;
5808     }
5810     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5811     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5812     int          prop       = GPOINTER_TO_INT(data);
5814     switch (prop)
5815     {
5816         case 0:
5817         {
5818             sp_repr_css_set_property (css, "writing-mode", "lr");
5819             break;
5820         }
5822         case 1:
5823         {
5824             sp_repr_css_set_property (css, "writing-mode", "tb");
5825             break;
5826         }
5827     }
5829     SPStyle *query =
5830         sp_style_new (SP_ACTIVE_DOCUMENT);
5831     int result_numbers =
5832         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5834     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5835     if (result_numbers == QUERY_STYLE_NOTHING)
5836     {
5837         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5838         prefs->setStyle("/tools/text/style", css);
5839     }
5841     sp_desktop_set_style (desktop, css, true, true);
5842     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5843                                    _("Text: Change orientation"));
5844     sp_repr_css_attr_unref (css);
5846     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5849 gboolean
5850 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5852     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5853     if (!desktop) return FALSE;
5855     switch (get_group0_keyval (event)) {
5856         case GDK_Escape: // defocus
5857             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5858             sp_text_toolbox_selection_changed (NULL, tbl); // update
5859             return TRUE; // I consumed the event
5860             break;
5861     }
5862     return FALSE;
5865 gboolean
5866 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5868     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5869     if (!desktop) return FALSE;
5871     switch (get_group0_keyval (event)) {
5872         case GDK_KP_Enter:
5873         case GDK_Return:
5874         case GDK_Escape: // defocus
5875             gtk_widget_hide (w);
5876             popdown_visible = false;
5877             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5878             return TRUE; // I consumed the event
5879             break;
5880         case GDK_w:
5881         case GDK_W:
5882             if (event->state & GDK_CONTROL_MASK) {
5883                 gtk_widget_hide (w);
5884                 popdown_visible = false;
5885                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5886                 return TRUE; // I consumed the event
5887             }
5888             break;
5889     }
5890     return FALSE;
5894 void
5895 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5896                                GObject     *tbl)
5898     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5900     if (g_object_get_data (tbl, "size-block")) return;
5902     // If this is not from selecting a size in the list (in which case get_active will give the
5903     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5904     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5905     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5906     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5907         return;
5909     gdouble value = -1;
5910     {
5911         gchar *endptr;
5912         gchar *const text = gtk_combo_box_get_active_text(cbox);
5913         if (text) {
5914             value = g_strtod(text, &endptr);
5915             if (endptr == text) {  // Conversion failed, non-numeric input.
5916                 value = -1;
5917             }
5918             g_free(text);
5919         }
5920     }
5921     if (value <= 0) {
5922         return; // could not parse value
5923     }
5925     SPCSSAttr *css = sp_repr_css_attr_new ();
5926     Inkscape::CSSOStringStream osfs;
5927     osfs << value;
5928     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5930     SPStyle *query =
5931         sp_style_new (SP_ACTIVE_DOCUMENT);
5932     int result_numbers =
5933         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5935     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5936     if (result_numbers == QUERY_STYLE_NOTHING)
5937     {
5938         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5939         prefs->setStyle("/tools/text/style", css);
5940     }
5942     sp_style_unref(query);
5944     sp_desktop_set_style (desktop, css, true, true);
5945     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5946                                    _("Text: Change font size"));
5947     sp_repr_css_attr_unref (css);
5949     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5952 gboolean
5953 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5955     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5956     if (!desktop) return FALSE;
5958     if (!g_object_get_data (tbl, "esc-pressed")) {
5959         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5960         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5961         sp_text_toolbox_size_changed (cbox, tbl);
5962         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5963     }
5964     return FALSE; // I consumed the event
5968 gboolean
5969 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5971     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5972     if (!desktop) return FALSE;
5974     switch (get_group0_keyval (event)) {
5975         case GDK_Escape: // defocus
5976             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5977             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5978             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5979             return TRUE; // I consumed the event
5980             break;
5981         case GDK_Return: // defocus
5982         case GDK_KP_Enter:
5983             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5984             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5985             sp_text_toolbox_size_changed (cbox, tbl);
5986             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5987             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5988             return TRUE; // I consumed the event
5989             break;
5990     }
5991     return FALSE;
5994 void
5995 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5996                                          GObject            *tbl)
5998     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5999     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6000     int x, y;
6002     if (!popdown_visible)
6003     {
6004         gdk_window_get_origin (widget->window, &x, &y);
6005         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6006         gtk_widget_show_all (popdown);
6007         //sp_transientize (popdown);
6009         gdk_pointer_grab (widget->window, TRUE,
6010                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6011                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6012                                         GDK_POINTER_MOTION_MASK),
6013                           NULL, NULL, GDK_CURRENT_TIME);
6015         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6017         popdown_visible = true;
6018     }
6019     else
6020     {
6021         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6022         gdk_pointer_ungrab (GDK_CURRENT_TIME);
6023         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6024         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6025         gtk_widget_hide (popdown);
6026         popdown_visible = false;
6027     }
6030 gboolean
6031 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6032                                  GdkEventFocus    */*event*/,
6033                                  GObject          */*tbl*/)
6035     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6036     return FALSE;
6039 gboolean
6040 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
6041                                    GdkEventFocus    */*event*/,
6042                                    GObject          */*tbl*/)
6044     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6046     if (popdown_hasfocus) {
6047         gtk_widget_hide (popdown);
6048         popdown_hasfocus = false;
6049         popdown_visible = false;
6050         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6051         return TRUE;
6052     }
6053     return FALSE;
6056 gboolean
6057 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
6058                                    GdkEventFocus    */*event*/,
6059                                    GObject          */*tbl*/)
6061     popdown_hasfocus = true;
6062     return TRUE;
6066 void
6067 cell_data_func  (GtkTreeViewColumn */*column*/,
6068                  GtkCellRenderer   *cell,
6069                  GtkTreeModel      *tree_model,
6070                  GtkTreeIter       *iter,
6071                  gpointer           /*data*/)
6073     gchar *family;
6074     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6075     gchar *const family_escaped = g_markup_escape_text(family, -1);
6077     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6078     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6080     std::stringstream markup;
6081     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6082            << family_escaped << "'>" << sample_escaped << "</span>";
6083     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6085     g_free(family);
6086     g_free(family_escaped);
6087     g_free(sample_escaped);
6090 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6091     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6092     if (completion) {
6093         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6094         g_object_unref (completion);
6095     }
6098 GtkWidget*
6099 sp_text_toolbox_new (SPDesktop *desktop)
6101     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6102     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6104     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6105     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6107     GtkTooltips *tt = gtk_tooltips_new();
6108     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6110     ////////////Family
6111     //Window
6112     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6113     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6115     //Entry
6116     GtkWidget           *entry = gtk_entry_new ();
6117     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6118     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6119     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6120     gtk_entry_completion_set_text_column (completion, 0);
6121     gtk_entry_completion_set_minimum_key_length (completion, 1);
6122     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6123     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6124     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6125     gtk_toolbar_append_widget( tbl, entry, "", "" );
6126     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6128     //Button
6129     GtkWidget   *button = gtk_button_new ();
6130     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6131     gtk_toolbar_append_widget( tbl, button, "", "");
6133     //Popdown
6134     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6135     GtkWidget           *treeview = gtk_tree_view_new ();
6137     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6138     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6139     gtk_tree_view_column_pack_start (column, cell, FALSE);
6140     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6141     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6142     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6144     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6145     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6146     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6148     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6150     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6151     gtk_container_add (GTK_CONTAINER (sw), treeview);
6153     gtk_container_add (GTK_CONTAINER (window), sw);
6154     gtk_widget_set_size_request (window, 300, 450);
6156     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6157     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6158     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6160     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6162     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6163     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6164     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6166     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6167     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6169     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6170     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6171     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6172     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6173     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6175     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6176     GtkWidget *box = gtk_event_box_new ();
6177     gtk_container_add (GTK_CONTAINER (box), image);
6178     gtk_toolbar_append_widget( tbl, box, "", "");
6179     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6180     GtkTooltips *tooltips = gtk_tooltips_new ();
6181     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6182     gtk_widget_hide (GTK_WIDGET (box));
6183     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6185     ////////////Size
6186     gchar const *const sizes[] = {
6187         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6188         "16", "18", "20", "22", "24", "28",
6189         "32", "36", "40", "48", "56", "64", "72", "144"
6190     };
6192     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6193     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6194         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6195     }
6196     gtk_widget_set_size_request (cbox, 80, -1);
6197     gtk_toolbar_append_widget( tbl, cbox, "", "");
6198     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6199     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6200     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6201     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6203     ////////////Text anchor
6204     GtkWidget *group   = gtk_radio_button_new (NULL);
6205     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6206     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6208     // left
6209     GtkWidget *rbutton = group;
6210     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6211     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6212     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6214     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6215     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6216     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6217     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6219     // center
6220     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6221     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6222     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6223     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6225     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6226     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6227     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6228     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6230     // right
6231     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6232     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6233     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6234     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6236     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6237     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6238     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6239     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6241     // fill
6242     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6243     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6244     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6245     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6247     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6248     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6249     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6250     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6252     gtk_toolbar_append_widget( tbl, row, "", "");
6254     //spacer
6255     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6257     ////////////Text style
6258     row = gtk_hbox_new (FALSE, 4);
6260     // bold
6261     rbutton = gtk_toggle_button_new ();
6262     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6263     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6264     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6265     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6267     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6268     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6269     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6271     // italic
6272     rbutton = gtk_toggle_button_new ();
6273     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6274     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6275     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6276     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6278     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6279     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6280     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6282     gtk_toolbar_append_widget( tbl, row, "", "");
6284     //spacer
6285     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6287     ////////////Text orientation
6288     group   = gtk_radio_button_new (NULL);
6289     row     = gtk_hbox_new (FALSE, 4);
6290     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6292     // horizontal
6293     rbutton = group;
6294     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6295     gtk_container_add           (GTK_CONTAINER (rbutton),
6296                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6297     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6298     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6300     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6301     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6302     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6304     // vertical
6305     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6306     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6307     gtk_container_add           (GTK_CONTAINER (rbutton),
6308                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6309     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6310     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6312     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6313     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6314     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6315     gtk_toolbar_append_widget( tbl, row, "", "" );
6318     //watch selection
6319     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6321     sigc::connection *c_selection_changed =
6322         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6323                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6324     pool->add_connection ("selection-changed", c_selection_changed);
6326     sigc::connection *c_selection_modified =
6327         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6328                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6329     pool->add_connection ("selection-modified", c_selection_modified);
6331     sigc::connection *c_subselection_changed =
6332         new sigc::connection (desktop->connectToolSubselectionChanged
6333                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6334     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6336     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6339     gtk_widget_show_all( GTK_WIDGET(tbl) );
6341     return GTK_WIDGET(tbl);
6342 } // end of sp_text_toolbox_new()
6344 }//<unnamed> namespace
6347 //#########################
6348 //##      Connector      ##
6349 //#########################
6351 static void sp_connector_path_set_avoid(void)
6353     cc_selection_set_avoid(true);
6357 static void sp_connector_path_set_ignore(void)
6359     cc_selection_set_avoid(false);
6364 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6366     // quit if run by the _changed callbacks
6367     if (g_object_get_data( tbl, "freeze" )) {
6368         return;
6369     }
6371     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6372     SPDocument *doc = sp_desktop_document(desktop);
6374     if (!sp_document_get_undo_sensitive(doc))
6375     {
6376         return;
6377     }
6379     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6381     if ( repr->attribute("inkscape:connector-spacing") ) {
6382         gdouble priorValue = gtk_adjustment_get_value(adj);
6383         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6384         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6385             return;
6386         }
6387     } else if ( adj->value == defaultConnSpacing ) {
6388         return;
6389     }
6391     // in turn, prevent callbacks from responding
6392     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6394     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6395     SP_OBJECT(desktop->namedview)->updateRepr();
6397     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6398     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6399         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6400         Geom::Matrix m = Geom::identity();
6401         avoid_item_move(&m, item);
6402     }
6404     if (items) {
6405         g_slist_free(items);
6406     }
6408     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6409             _("Change connector spacing"));
6411     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6413     spinbutton_defocus(GTK_OBJECT(tbl));
6416 static void sp_connector_graph_layout(void)
6418     if (!SP_ACTIVE_DESKTOP) return;
6419     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6421     // hack for clones, see comment in align-and-distribute.cpp
6422     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6423     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6425     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6427     prefs->setInt("/options/clonecompensation/value", saved_compensation);
6429     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6432 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6434     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6435     prefs->setBool("/tools/connector/directedlayout",
6436                 gtk_toggle_action_get_active( act ));
6439 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6441     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6442     prefs->setBool("/tools/connector/avoidoverlaplayout",
6443                 gtk_toggle_action_get_active( act ));
6447 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6449     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6450     prefs->setDouble("/tools/connector/length", adj->value);
6451     spinbutton_defocus(GTK_OBJECT(tbl));
6454 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6455                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6456                                             bool /*is_interactive*/, gpointer data)
6458     GtkWidget *tbl = GTK_WIDGET(data);
6460     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6461         return;
6462     }
6463     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6464         return;
6465     }
6467     GtkAdjustment *adj = (GtkAdjustment*)
6468             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6469     gdouble spacing = defaultConnSpacing;
6470     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6472     gtk_adjustment_set_value(adj, spacing);
6476 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6477     NULL, /* child_added */
6478     NULL, /* child_removed */
6479     connector_tb_event_attr_changed,
6480     NULL, /* content_changed */
6481     NULL  /* order_changed */
6482 };
6485 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6487     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6488     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6490     {
6491         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6492                                           _("Avoid"),
6493                                           _("Make connectors avoid selected objects"),
6494                                           "connector_avoid",
6495                                           secondarySize );
6496         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6497         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6498     }
6500     {
6501         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6502                                           _("Ignore"),
6503                                           _("Make connectors ignore selected objects"),
6504                                           "connector_ignore",
6505                                           secondarySize );
6506         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6507         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6508     }
6510     EgeAdjustmentAction* eact = 0;
6512     // Spacing spinbox
6513     eact = create_adjustment_action( "ConnectorSpacingAction",
6514                                      _("Connector Spacing"), _("Spacing:"),
6515                                      _("The amount of space left around objects by auto-routing connectors"),
6516                                      "/tools/connector/spacing", defaultConnSpacing,
6517                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6518                                      0, 100, 1.0, 10.0,
6519                                      0, 0, 0,
6520                                      connector_spacing_changed, 1, 0 );
6521     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6523     // Graph (connector network) layout
6524     {
6525         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6526                                           _("Graph"),
6527                                           _("Nicely arrange selected connector network"),
6528                                           "graph_layout",
6529                                           secondarySize );
6530         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6531         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6532     }
6534     // Default connector length spinbox
6535     eact = create_adjustment_action( "ConnectorLengthAction",
6536                                      _("Connector Length"), _("Length:"),
6537                                      _("Ideal length for connectors when layout is applied"),
6538                                      "/tools/connector/length", 100,
6539                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6540                                      10, 1000, 10.0, 100.0,
6541                                      0, 0, 0,
6542                                      connector_length_changed, 1, 0 );
6543     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6546     // Directed edges toggle button
6547     {
6548         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6549                                                       _("Downwards"),
6550                                                       _("Make connectors with end-markers (arrows) point downwards"),
6551                                                       "directed_graph",
6552                                                       Inkscape::ICON_SIZE_DECORATION );
6553         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6555         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
6556         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
6558         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6559     }
6561     // Avoid overlaps toggle button
6562     {
6563         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6564                                                       _("Remove overlaps"),
6565                                                       _("Do not allow overlapping shapes"),
6566                                                       "remove_overlaps",
6567                                                       Inkscape::ICON_SIZE_DECORATION );
6568         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6570         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
6571         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
6573         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6574     }
6576     // Code to watch for changes to the connector-spacing attribute in
6577     // the XML.
6578     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6579     g_assert(repr != NULL);
6581     purge_repr_listener( holder, holder );
6583     if (repr) {
6584         g_object_set_data( holder, "repr", repr );
6585         Inkscape::GC::anchor(repr);
6586         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6587         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6588     }
6589 } // end of sp_connector_toolbox_prep()
6592 //#########################
6593 //##     Paintbucket     ##
6594 //#########################
6596 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6598     gint channels = ege_select_one_action_get_active( act );
6599     flood_channels_set_channels( channels );
6602 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6604     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6605     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
6608 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6610     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6611     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
6614 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6616     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6617     SPUnit const *unit = tracker->getActiveUnit();
6618     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6620     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6621     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
6624 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6626     // FIXME: make defaults settable via Inkscape Options
6627     struct KeyValue {
6628         char const *key;
6629         double value;
6630     } const key_values[] = {
6631         {"threshold", 15},
6632         {"offset", 0.0}
6633     };
6635     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6636         KeyValue const &kv = key_values[i];
6637         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6638         if ( adj ) {
6639             gtk_adjustment_set_value(adj, kv.value);
6640         }
6641     }
6643     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6644     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6645     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6646     ege_select_one_action_set_active( autogap_action, 0 );
6649 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6651     EgeAdjustmentAction* eact = 0;
6652     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6654     {
6655         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6657         GList* items = 0;
6658         gint count = 0;
6659         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6660         {
6661             GtkTreeIter iter;
6662             gtk_list_store_append( model, &iter );
6663             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6664             count++;
6665         }
6666         g_list_free( items );
6667         items = 0;
6668         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6669         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6670         ege_select_one_action_set_appearance( act1, "compact" );
6671         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
6672         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6673         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6674         g_object_set_data( holder, "channels_action", act1 );
6675     }
6677     // Spacing spinbox
6678     {
6679         eact = create_adjustment_action(
6680             "ThresholdAction",
6681             _("Fill Threshold"), _("Threshold:"),
6682             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6683             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6684             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6685             0, 0, 0,
6686             paintbucket_threshold_changed, 1, 0 );
6688         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6689         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6690     }
6692     // Create the units menu.
6693     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6694     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
6695     if (!stored_unit.empty())
6696         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
6697     g_object_set_data( holder, "tracker", tracker );
6698     {
6699         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6700         gtk_action_group_add_action( mainActions, act );
6701     }
6703     // Offset spinbox
6704     {
6705         eact = create_adjustment_action(
6706             "OffsetAction",
6707             _("Grow/shrink by"), _("Grow/shrink by:"),
6708             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6709             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6710             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6711             0, 0, 0,
6712             paintbucket_offset_changed, 1, 2);
6713         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6715         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6716     }
6718     /* Auto Gap */
6719     {
6720         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6722         GList* items = 0;
6723         gint count = 0;
6724         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6725         {
6726             GtkTreeIter iter;
6727             gtk_list_store_append( model, &iter );
6728             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6729             count++;
6730         }
6731         g_list_free( items );
6732         items = 0;
6733         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6734         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6735         ege_select_one_action_set_appearance( act2, "compact" );
6736         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
6737         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6738         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6739         g_object_set_data( holder, "autogap_action", act2 );
6740     }
6742     /* Reset */
6743     {
6744         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6745                                           _("Defaults"),
6746                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6747                                           GTK_STOCK_CLEAR );
6748         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6749         gtk_action_group_add_action( mainActions, act );
6750         gtk_action_set_sensitive( act, TRUE );
6751     }
6755 /*
6756   Local Variables:
6757   mode:c++
6758   c-file-style:"stroustrup"
6759   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6760   indent-tabs-mode:nil
6761   fill-column:99
6762   End:
6763 */
6764 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :