Code

929af817a0a7c1b274b1975a10892e4cb8d8ba1e
[inkscape.git] / src / widgets / toolbox.cpp
1 /** \file
2  * Controls bars for some of Inkscape's tools
3  * (for some tools, they are in their own files)
4  */
6 /*
7 *
8 * Authors:
9 *   MenTaLguY <mental@rydia.net>
10 *   Lauris Kaplinski <lauris@kaplinski.com>
11 *   bulia byak <buliabyak@users.sf.net>
12 *   Frank Felfe <innerspace@iname.com>
13 *   John Cliff <simarilius@yahoo.com>
14 *   David Turner <novalis@gnu.org>
15 *   Josh Andler <scislac@scislac.com>
16 *   Jon A. Cruz <jon@joncruz.org>
17 *   Maximilian Albert <maximilian.albert@gmail.com>
18 *
19 * Copyright (C) 2004 David Turner
20 * Copyright (C) 2003 MenTaLguY
21 * Copyright (C) 1999-2008 authors
22 * Copyright (C) 2001-2002 Ximian, Inc.
23 *
24 * Released under GNU GPL, read the file 'COPYING' for more information
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <cstring>
32 #include <string>
34 #include <gtkmm.h>
35 #include <gtk/gtk.h>
36 #include <iostream>
37 #include <sstream>
39 #include "widgets/button.h"
40 #include "widgets/widget-sizes.h"
41 #include "widgets/spw-utilities.h"
42 #include "widgets/spinbutton-events.h"
43 #include "dialogs/text-edit.h"
44 #include "dialogs/dialog-events.h"
46 #include "ui/widget/style-swatch.h"
48 #include "verbs.h"
49 #include "sp-namedview.h"
50 #include "desktop.h"
51 #include "desktop-handles.h"
52 #include "xml/repr.h"
53 #include "xml/node-event-vector.h"
54 #include "xml/attribute-record.h"
55 #include <glibmm/i18n.h>
56 #include "helper/unit-menu.h"
57 #include "helper/units.h"
58 #include "live_effects/effect.h"
60 #include "inkscape.h"
61 #include "conn-avoid-ref.h"
64 #include "select-toolbar.h"
65 #include "gradient-toolbar.h"
67 #include "connector-context.h"
68 #include "node-context.h"
69 #include "pen-context.h"
70 #include "lpe-tool-context.h"
71 #include "live_effects/lpe-line_segment.h"
72 #include "shape-editor.h"
73 #include "tweak-context.h"
74 #include "sp-rect.h"
75 #include "box3d.h"
76 #include "box3d-context.h"
77 #include "sp-star.h"
78 #include "sp-spiral.h"
79 #include "sp-ellipse.h"
80 #include "sp-text.h"
81 #include "sp-flowtext.h"
82 #include "sp-clippath.h"
83 #include "sp-mask.h"
84 #include "style.h"
85 #include "tools-switch.h"
86 #include "selection.h"
87 #include "selection-chemistry.h"
88 #include "document-private.h"
89 #include "desktop-style.h"
90 #include "../libnrtype/font-lister.h"
91 #include "../libnrtype/font-instance.h"
92 #include "../connection-pool.h"
93 #include "../preferences.h"
94 #include "../inkscape-stock.h"
95 #include "icon.h"
96 #include "graphlayout/graphlayout.h"
97 #include "interface.h"
98 #include "shortcuts.h"
100 #include "mod360.h"
102 #include "toolbox.h"
104 #include "flood-context.h"
106 #include "ink-action.h"
107 #include "ege-adjustment-action.h"
108 #include "ege-output-action.h"
109 #include "ege-select-one-action.h"
110 #include "helper/unit-tracker.h"
111 #include "live_effects/lpe-angle_bisector.h"
113 #include "svg/css-ostringstream.h"
115 #include "widgets/calligraphic-profile-rename.h"
117 using Inkscape::UnitTracker;
119 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
120 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
122 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
135 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
143 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
144     static Inkscape::IconSize sizeChoices[] = {
145         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
146         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
147         Inkscape::ICON_SIZE_MENU
148     };
149     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
150     int index = prefs->getIntLimited( path, attr, base, 0, G_N_ELEMENTS(sizeChoices) );
151     return sizeChoices[index];
154 static struct {
155     gchar const *type_name;
156     gchar const *data_name;
157     sp_verb_t verb;
158     sp_verb_t doubleclick_verb;
159 } const tools[] = {
160     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
161     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
162     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
163     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
164     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
165     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
166     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
167     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
168     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
169     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
170     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
171     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
172     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
173     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
174     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
175     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
176     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
177     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
178     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
179     { NULL, NULL, 0, 0 }
180 };
182 static struct {
183     gchar const *type_name;
184     gchar const *data_name;
185     GtkWidget *(*create_func)(SPDesktop *desktop);
186     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
187     gchar const *ui_name;
188     gint swatch_verb_id;
189     gchar const *swatch_tool;
190     gchar const *swatch_tip;
191 } const aux_toolboxes[] = {
192     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
193       SP_VERB_INVALID, 0, 0},
194     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
195       SP_VERB_INVALID, 0, 0},
196     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
197       SP_VERB_CONTEXT_TWEAK_PREFS, "tools.tweak", N_("Color/opacity used for color tweaking")},
198     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
199       SP_VERB_INVALID, 0, 0},
200     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
201       SP_VERB_CONTEXT_STAR_PREFS,   "tools.shapes.star",     N_("Style of new stars")},
202     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
203       SP_VERB_CONTEXT_RECT_PREFS,   "tools.shapes.rect",     N_("Style of new rectangles")},
204     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
205       SP_VERB_CONTEXT_3DBOX_PREFS,  "tools.shapes.3dbox",    N_("Style of new 3D boxes")},
206     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
207       SP_VERB_CONTEXT_ARC_PREFS,    "tools.shapes.arc",      N_("Style of new ellipses")},
208     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
209       SP_VERB_CONTEXT_SPIRAL_PREFS, "tools.shapes.spiral",   N_("Style of new spirals")},
210     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
211       SP_VERB_CONTEXT_PENCIL_PREFS, "tools.freehand.pencil", N_("Style of new paths created by Pencil")},
212     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
213       SP_VERB_CONTEXT_PEN_PREFS,    "tools.freehand.pen",    N_("Style of new paths created by Pen")},
214     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
215       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "tools.calligraphic", N_("Style of new calligraphic strokes")},
216     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
217       SP_VERB_CONTEXT_ERASER_PREFS, "tools.eraser", _("TBD")},
218     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
219       SP_VERB_CONTEXT_LPETOOL_PREFS, "tools.lpetool", _("TBD")},
220     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
221       SP_VERB_INVALID, 0, 0},
222     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
223       SP_VERB_INVALID, 0, 0},
224     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
225       SP_VERB_INVALID, 0, 0},
226     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
227       SP_VERB_INVALID, 0, 0},
228     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
229       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "tools.paintbucket", N_("Style of Paint Bucket fill objects")},
230     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
231 };
233 #define TOOLBAR_SLIDER_HINT "full"
235 static gchar const * ui_descr =
236         "<ui>"
237         "  <toolbar name='SelectToolbar'>"
238         "    <toolitem action='EditSelectAll' />"
239         "    <toolitem action='EditSelectAllInAllLayers' />"
240         "    <toolitem action='EditDeselect' />"
241         "    <separator />"
242         "    <toolitem action='ObjectRotate90CCW' />"
243         "    <toolitem action='ObjectRotate90' />"
244         "    <toolitem action='ObjectFlipHorizontally' />"
245         "    <toolitem action='ObjectFlipVertically' />"
246         "    <separator />"
247         "    <toolitem action='SelectionToBack' />"
248         "    <toolitem action='SelectionLower' />"
249         "    <toolitem action='SelectionRaise' />"
250         "    <toolitem action='SelectionToFront' />"
251         "    <separator />"
252         "    <toolitem action='XAction' />"
253         "    <toolitem action='YAction' />"
254         "    <toolitem action='WidthAction' />"
255         "    <toolitem action='LockAction' />"
256         "    <toolitem action='HeightAction' />"
257         "    <toolitem action='UnitsAction' />"
258         "    <separator />"
259         "    <toolitem action='transform_affect_label' />"
260         "    <toolitem action='transform_stroke' />"
261         "    <toolitem action='transform_corners' />"
262         "    <toolitem action='transform_gradient' />"
263         "    <toolitem action='transform_pattern' />"
264         "  </toolbar>"
266         "  <toolbar name='NodeToolbar'>"
267         "    <toolitem action='NodeInsertAction' />"
268         "    <toolitem action='NodeDeleteAction' />"
269         "    <separator />"
270         "    <toolitem action='NodeJoinAction' />"
271         "    <toolitem action='NodeBreakAction' />"
272         "    <separator />"
273         "    <toolitem action='NodeJoinSegmentAction' />"
274         "    <toolitem action='NodeDeleteSegmentAction' />"
275         "    <separator />"
276         "    <toolitem action='NodeCuspAction' />"
277         "    <toolitem action='NodeSmoothAction' />"
278         "    <toolitem action='NodeSymmetricAction' />"
279         "    <separator />"
280         "    <toolitem action='NodeLineAction' />"
281         "    <toolitem action='NodeCurveAction' />"
282         "    <separator />"
283         "    <toolitem action='ObjectToPath' />"
284         "    <toolitem action='StrokeToPath' />"
285         "    <separator />"
286         "    <toolitem action='NodeXAction' />"
287         "    <toolitem action='NodeYAction' />"
288         "    <toolitem action='NodeUnitsAction' />"
289         "    <separator />"
290         "    <toolitem action='ObjectEditClipPathAction' />"
291         "    <toolitem action='ObjectEditMaskPathAction' />"
292         "    <toolitem action='EditNextLPEParameterAction' />"
293         "    <separator />"
294         "    <toolitem action='NodesShowHandlesAction' />"
295         "    <toolitem action='NodesShowHelperpath' />"
296         "  </toolbar>"
298         "  <toolbar name='TweakToolbar'>"
299         "    <toolitem action='TweakWidthAction' />"
300         "    <separator />"
301         "    <toolitem action='TweakForceAction' />"
302         "    <toolitem action='TweakPressureAction' />"
303         "    <separator />"
304         "    <toolitem action='TweakModeAction' />"
305         "    <separator />"
306         "    <toolitem action='TweakFidelityAction' />"
307         "    <separator />"
308         "    <toolitem action='TweakChannelsLabel' />"
309         "    <toolitem action='TweakDoH' />"
310         "    <toolitem action='TweakDoS' />"
311         "    <toolitem action='TweakDoL' />"
312         "    <toolitem action='TweakDoO' />"
313         "  </toolbar>"
315         "  <toolbar name='ZoomToolbar'>"
316         "    <toolitem action='ZoomIn' />"
317         "    <toolitem action='ZoomOut' />"
318         "    <separator />"
319         "    <toolitem action='Zoom1:0' />"
320         "    <toolitem action='Zoom1:2' />"
321         "    <toolitem action='Zoom2:1' />"
322         "    <separator />"
323         "    <toolitem action='ZoomSelection' />"
324         "    <toolitem action='ZoomDrawing' />"
325         "    <toolitem action='ZoomPage' />"
326         "    <toolitem action='ZoomPageWidth' />"
327         "    <separator />"
328         "    <toolitem action='ZoomPrev' />"
329         "    <toolitem action='ZoomNext' />"
330         "  </toolbar>"
332         "  <toolbar name='StarToolbar'>"
333         "    <separator />"
334         "    <toolitem action='StarStateAction' />"
335         "    <separator />"
336         "    <toolitem action='FlatAction' />"
337         "    <separator />"
338         "    <toolitem action='MagnitudeAction' />"
339         "    <toolitem action='SpokeAction' />"
340         "    <toolitem action='RoundednessAction' />"
341         "    <toolitem action='RandomizationAction' />"
342         "    <separator />"
343         "    <toolitem action='StarResetAction' />"
344         "  </toolbar>"
346         "  <toolbar name='RectToolbar'>"
347         "    <toolitem action='RectStateAction' />"
348         "    <toolitem action='RectWidthAction' />"
349         "    <toolitem action='RectHeightAction' />"
350         "    <toolitem action='RadiusXAction' />"
351         "    <toolitem action='RadiusYAction' />"
352         "    <toolitem action='RectUnitsAction' />"
353         "    <separator />"
354         "    <toolitem action='RectResetAction' />"
355         "  </toolbar>"
357         "  <toolbar name='3DBoxToolbar'>"
358         "    <toolitem action='3DBoxAngleXAction' />"
359         "    <toolitem action='3DBoxVPXStateAction' />"
360         "    <separator />"
361         "    <toolitem action='3DBoxAngleYAction' />"
362         "    <toolitem action='3DBoxVPYStateAction' />"
363         "    <separator />"
364         "    <toolitem action='3DBoxAngleZAction' />"
365         "    <toolitem action='3DBoxVPZStateAction' />"
366         "  </toolbar>"
368         "  <toolbar name='SpiralToolbar'>"
369         "    <toolitem action='SpiralStateAction' />"
370         "    <toolitem action='SpiralRevolutionAction' />"
371         "    <toolitem action='SpiralExpansionAction' />"
372         "    <toolitem action='SpiralT0Action' />"
373         "    <separator />"
374         "    <toolitem action='SpiralResetAction' />"
375         "  </toolbar>"
377         "  <toolbar name='PenToolbar'>"
378         "    <toolitem action='FreehandModeActionPen' />"
379         "    <separator />"
380         "    <toolitem action='SetPenShapeAction'/>"
381         "  </toolbar>"
383         "  <toolbar name='PencilToolbar'>"
384         "    <toolitem action='FreehandModeActionPencil' />"
385         "    <separator />"
386         "    <toolitem action='PencilToleranceAction' />"
387         "    <separator />"
388         "    <toolitem action='PencilResetAction' />"
389         "    <separator />"
390         "    <toolitem action='SetPencilShapeAction'/>"
391         "  </toolbar>"
393         "  <toolbar name='CalligraphyToolbar'>"
394         "    <separator />"
395         "    <toolitem action='SetProfileAction'/>"
396         "    <separator />"
397         "    <toolitem action='CalligraphyWidthAction' />"
398         "    <toolitem action='PressureAction' />"
399         "    <toolitem action='TraceAction' />"
400         "    <toolitem action='ThinningAction' />"
401         "    <separator />"
402         "    <toolitem action='AngleAction' />"
403         "    <toolitem action='TiltAction' />"
404         "    <toolitem action='FixationAction' />"
405         "    <separator />"
406         "    <toolitem action='CapRoundingAction' />"
407         "    <separator />"
408         "    <toolitem action='TremorAction' />"
409         "    <toolitem action='WiggleAction' />"
410         "    <toolitem action='MassAction' />"
411         "    <separator />"
412         "  </toolbar>"
414         "  <toolbar name='ArcToolbar'>"
415         "    <toolitem action='ArcStateAction' />"
416         "    <separator />"
417         "    <toolitem action='ArcStartAction' />"
418         "    <toolitem action='ArcEndAction' />"
419         "    <separator />"
420         "    <toolitem action='ArcOpenAction' />"
421         "    <separator />"
422         "    <toolitem action='ArcResetAction' />"
423         "    <separator />"
424         "  </toolbar>"
426         "  <toolbar name='PaintbucketToolbar'>"
427         "    <toolitem action='ChannelsAction' />"
428         "    <separator />"
429         "    <toolitem action='ThresholdAction' />"
430         "    <separator />"
431         "    <toolitem action='OffsetAction' />"
432         "    <toolitem action='PaintbucketUnitsAction' />"
433         "    <separator />"
434         "    <toolitem action='AutoGapAction' />"
435         "    <separator />"
436         "    <toolitem action='PaintbucketResetAction' />"
437         "  </toolbar>"
439         "  <toolbar name='EraserToolbar'>"
440         "    <toolitem action='EraserWidthAction' />"
441         "    <separator />"
442         "    <toolitem action='EraserModeAction' />"
443         "  </toolbar>"
445         "  <toolbar name='LPEToolToolbar'>"
446         "    <toolitem action='LPEToolModeAction' />"
447         "    <separator />"
448         "    <toolitem action='LPEShowBBoxAction' />"
449         "    <toolitem action='LPEBBoxFromSelectionAction' />"
450         "    <separator />"
451         "    <toolitem action='LPELineSegmentAction' />"
452         "    <separator />"
453         "    <toolitem action='LPEMeasuringAction' />"
454         "    <toolitem action='LPEToolUnitsAction' />"
455         "    <separator />"
456         "    <toolitem action='LPEOpenLPEDialogAction' />"
457         "  </toolbar>"
459         "  <toolbar name='DropperToolbar'>"
460         "    <toolitem action='DropperOpacityAction' />"
461         "    <toolitem action='DropperPickAlphaAction' />"
462         "    <toolitem action='DropperSetAlphaAction' />"
463         "  </toolbar>"
465         "  <toolbar name='ConnectorToolbar'>"
466         "    <toolitem action='ConnectorAvoidAction' />"
467         "    <toolitem action='ConnectorIgnoreAction' />"
468         "    <toolitem action='ConnectorSpacingAction' />"
469         "    <toolitem action='ConnectorGraphAction' />"
470         "    <toolitem action='ConnectorLengthAction' />"
471         "    <toolitem action='ConnectorDirectedAction' />"
472         "    <toolitem action='ConnectorOverlapAction' />"
473         "  </toolbar>"
475         "</ui>"
478 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
480 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
482 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
483 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
485 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
486 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
488 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
489 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
492 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
493                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
494                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
496 class VerbAction : public Gtk::Action {
497 public:
498     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
500     virtual ~VerbAction();
501     virtual void set_active(bool active = true);
503 protected:
504     virtual Gtk::Widget* create_menu_item_vfunc();
505     virtual Gtk::Widget* create_tool_item_vfunc();
507     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
508     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
510     virtual void on_activate();
512 private:
513     Inkscape::Verb* verb;
514     Inkscape::Verb* verb2;
515     Inkscape::UI::View::View *view;
516     GtkTooltips *tooltips;
517     bool active;
519     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
520 };
523 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
525     Glib::RefPtr<VerbAction> result;
526     SPAction *action = verb->get_action(view);
527     if ( action ) {
528         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
529         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
530     }
532     return result;
535 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
536     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
537     verb(verb),
538     verb2(verb2),
539     view(view),
540     tooltips(tooltips),
541     active(false)
545 VerbAction::~VerbAction()
549 Gtk::Widget* VerbAction::create_menu_item_vfunc()
551 // First call in to get the icon rendered if present in SVG
552     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
553     delete widget;
554     widget = 0;
556     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
557 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
558     return widg;
561 Gtk::Widget* VerbAction::create_tool_item_vfunc()
563 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
564     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
565     GtkWidget* toolbox = 0;
566     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
567                                                                           SP_BUTTON_TYPE_TOGGLE,
568                                                                           verb,
569                                                                           verb2,
570                                                                           view,
571                                                                           tooltips );
572     if ( active ) {
573         sp_button_toggle_set_down( SP_BUTTON(button), active);
574     }
575     gtk_widget_show_all( button );
576     Gtk::Widget* wrapped = Glib::wrap(button);
577     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
578     holder->add(*wrapped);
580 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
581     return holder;
584 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
586 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
587     Gtk::Action::connect_proxy_vfunc(proxy);
590 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
592 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
593     Gtk::Action::disconnect_proxy_vfunc(proxy);
596 void VerbAction::set_active(bool active)
598     this->active = active;
599     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
600     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
601         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
602         if (ti) {
603             // *should* have one child that is the SPButton
604             Gtk::Widget* child = ti->get_child();
605             if ( child && SP_IS_BUTTON(child->gobj()) ) {
606                 SPButton* button = SP_BUTTON(child->gobj());
607                 sp_button_toggle_set_down( button, active );
608             }
609         }
610     }
613 void VerbAction::on_activate()
615     if ( verb ) {
616         SPAction *action = verb->get_action(view);
617         if ( action ) {
618             sp_action_perform(action, 0);
619         }
620     }
623 /* Global text entry widgets necessary for update */
624 /* GtkWidget *dropper_rgb_entry,
625           *dropper_opacity_entry ; */
626 // should be made a private member once this is converted to class
628 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
629     connection->disconnect();
630     delete connection;
633 static void purge_repr_listener( GObject* obj, GObject* tbl )
635     (void)obj;
636     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
637     if (oldrepr) { // remove old listener
638         sp_repr_remove_listener_by_data(oldrepr, tbl);
639         Inkscape::GC::release(oldrepr);
640         oldrepr = 0;
641         g_object_set_data( tbl, "repr", NULL );
642     }
645 GtkWidget *
646 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
647                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
648                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
650     SPAction *action = verb->get_action(view);
651     if (!action) return NULL;
653     SPAction *doubleclick_action;
654     if (doubleclick_verb)
655         doubleclick_action = doubleclick_verb->get_action(view);
656     else
657         doubleclick_action = NULL;
659     /* fixme: Handle sensitive/unsensitive */
660     /* fixme: Implement sp_button_new_from_action */
661     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
662     gtk_widget_show(b);
665     unsigned int shortcut = sp_shortcut_get_primary(verb);
666     if (shortcut) {
667         gchar key[256];
668         sp_ui_shortcut_string(shortcut, key);
669         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
670         if ( t ) {
671             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
672         }
673         g_free(tip);
674     } else {
675         if ( t ) {
676             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
677         }
678     }
680     return b;
684 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
686     SPAction* targetAction = SP_ACTION(user_data);
687     if ( targetAction ) {
688         sp_action_perform( targetAction, NULL );
689     }
692 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
694     if ( data ) {
695         GtkAction* act = GTK_ACTION(data);
696         gtk_action_set_sensitive( act, sensitive );
697     }
700 static SPActionEventVector action_event_vector = {
701     {NULL},
702     NULL,
703     NULL,
704     sp_action_action_set_sensitive,
705     NULL,
706     NULL
707 };
709 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
711     GtkAction* act = 0;
713     SPAction* targetAction = verb->get_action(view);
714     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
715     act = GTK_ACTION(inky);
716     gtk_action_set_sensitive( act, targetAction->sensitive );
718     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
720     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
721     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
723     return act;
726 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
728     Inkscape::UI::View::View *view = desktop;
729     gint verbsToUse[] = {
730         // disabled until we have icons for them:
731         //find
732         //SP_VERB_EDIT_TILE,
733         //SP_VERB_EDIT_UNTILE,
734         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
735         SP_VERB_DIALOG_DISPLAY,
736         SP_VERB_DIALOG_FILL_STROKE,
737         SP_VERB_DIALOG_NAMEDVIEW,
738         SP_VERB_DIALOG_TEXT,
739         SP_VERB_DIALOG_XML_EDITOR,
740         SP_VERB_EDIT_CLONE,
741         SP_VERB_EDIT_COPY,
742         SP_VERB_EDIT_CUT,
743         SP_VERB_EDIT_DUPLICATE,
744         SP_VERB_EDIT_PASTE,
745         SP_VERB_EDIT_REDO,
746         SP_VERB_EDIT_UNDO,
747         SP_VERB_EDIT_UNLINK_CLONE,
748         SP_VERB_FILE_EXPORT,
749         SP_VERB_FILE_IMPORT,
750         SP_VERB_FILE_NEW,
751         SP_VERB_FILE_OPEN,
752         SP_VERB_FILE_PRINT,
753         SP_VERB_FILE_SAVE,
754         SP_VERB_OBJECT_TO_CURVE,
755         SP_VERB_SELECTION_GROUP,
756         SP_VERB_SELECTION_OUTLINE,
757         SP_VERB_SELECTION_UNGROUP,
758         SP_VERB_ZOOM_1_1,
759         SP_VERB_ZOOM_1_2,
760         SP_VERB_ZOOM_2_1,
761         SP_VERB_ZOOM_DRAWING,
762         SP_VERB_ZOOM_IN,
763         SP_VERB_ZOOM_NEXT,
764         SP_VERB_ZOOM_OUT,
765         SP_VERB_ZOOM_PAGE,
766         SP_VERB_ZOOM_PAGE_WIDTH,
767         SP_VERB_ZOOM_PREV,
768         SP_VERB_ZOOM_SELECTION,
769     };
771     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
773     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
774     Glib::RefPtr<Gtk::ActionGroup> mainActions;
775     if ( groups.find(desktop) != groups.end() ) {
776         mainActions = groups[desktop];
777     }
779     if ( !mainActions ) {
780         mainActions = Gtk::ActionGroup::create("main");
781         groups[desktop] = mainActions;
782     }
784     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
785         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
786         if ( verb ) {
787             if (!mainActions->get_action(verb->get_id())) {
788                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
789                 mainActions->add(Glib::wrap(act));
790             }
791         }
792     }
794     if ( !mainActions->get_action("ToolZoom") ) {
795         GtkTooltips *tt = gtk_tooltips_new();
796         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
797             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
798             if ( va ) {
799                 mainActions->add(va);
800                 if ( i == 0 ) {
801                     va->set_active(true);
802                 }
803             }
804         }
805     }
808     return mainActions;
812 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
814     gtk_widget_set_size_request( widget,
815                                  widget->allocation.width,
816                                  widget->allocation.height );
819 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
821     gtk_widget_set_size_request( widget, -1, -1 );
826 GtkWidget *
827 sp_tool_toolbox_new()
829     GtkTooltips *tt = gtk_tooltips_new();
830     GtkWidget* tb = gtk_toolbar_new();
831     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
832     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
834     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
835     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
837     gtk_widget_set_sensitive(tb, FALSE);
839     GtkWidget *hb = gtk_handle_box_new();
840     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
841     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
842     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
844     gtk_container_add(GTK_CONTAINER(hb), tb);
845     gtk_widget_show(GTK_WIDGET(tb));
847     sigc::connection* conn = new sigc::connection;
848     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
850     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
851     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
853     return hb;
856 GtkWidget *
857 sp_aux_toolbox_new()
859     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
861     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
863     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
865     gtk_widget_set_sensitive(tb, FALSE);
867     GtkWidget *hb = gtk_handle_box_new();
868     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
869     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
870     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
872     gtk_container_add(GTK_CONTAINER(hb), tb);
873     gtk_widget_show(GTK_WIDGET(tb));
875     sigc::connection* conn = new sigc::connection;
876     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
878     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
879     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
881     return hb;
884 //####################################
885 //# Commands Bar
886 //####################################
888 GtkWidget *
889 sp_commands_toolbox_new()
891     GtkWidget *tb = gtk_toolbar_new();
893     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
894     gtk_widget_set_sensitive(tb, FALSE);
896     GtkWidget *hb = gtk_handle_box_new();
897     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
898     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
899     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
901     gtk_container_add(GTK_CONTAINER(hb), tb);
902     gtk_widget_show(GTK_WIDGET(tb));
904     sigc::connection* conn = new sigc::connection;
905     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
907     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
908     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
910     return hb;
914 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
915                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
916                                                        gchar const *path, gchar const *data, gdouble def,
917                                                        GtkWidget *focusTarget,
918                                                        GtkWidget *us,
919                                                        GObject *dataKludge,
920                                                        gboolean altx, gchar const *altx_mark,
921                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
922                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
923                                                        void (*callback)(GtkAdjustment *, GObject *),
924                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
926         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
927     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, data, def) * factor,
928                                                              lower, upper, step, page, page ) );
929     if (us) {
930         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
931     }
933     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
935     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
936     if ( shortLabel ) {
937         g_object_set( act, "short_label", shortLabel, NULL );
938     }
940     if ( (descrCount > 0) && descrLabels && descrValues ) {
941         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
942     }
944     if ( focusTarget ) {
945         ege_adjustment_action_set_focuswidget( act, focusTarget );
946     }
948     if ( altx && altx_mark ) {
949         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
950     }
952     if ( dataKludge ) {
953         g_object_set_data( dataKludge, data, adj );
954     }
956     // Using a cast just to make sure we pass in the right kind of function pointer
957     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
959     return act;
963 //####################################
964 //# node editing callbacks
965 //####################################
967 /**
968  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
969  */
970 static ShapeEditor *get_current_shape_editor()
972     if (!SP_ACTIVE_DESKTOP) {
973         return NULL;
974     }
976     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
978     if (!SP_IS_NODE_CONTEXT(event_context)) {
979         return NULL;
980     }
982     return SP_NODE_CONTEXT(event_context)->shape_editor;
986 void
987 sp_node_path_edit_add(void)
989     ShapeEditor *shape_editor = get_current_shape_editor();
990     if (shape_editor) shape_editor->add_node();
993 void
994 sp_node_path_edit_delete(void)
996     ShapeEditor *shape_editor = get_current_shape_editor();
997     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1000 void
1001 sp_node_path_edit_delete_segment(void)
1003     ShapeEditor *shape_editor = get_current_shape_editor();
1004     if (shape_editor) shape_editor->delete_segment();
1007 void
1008 sp_node_path_edit_break(void)
1010     ShapeEditor *shape_editor = get_current_shape_editor();
1011     if (shape_editor) shape_editor->break_at_nodes();
1014 void
1015 sp_node_path_edit_join(void)
1017     ShapeEditor *shape_editor = get_current_shape_editor();
1018     if (shape_editor) shape_editor->join_nodes();
1021 void
1022 sp_node_path_edit_join_segment(void)
1024     ShapeEditor *shape_editor = get_current_shape_editor();
1025     if (shape_editor) shape_editor->join_segments();
1028 void
1029 sp_node_path_edit_toline(void)
1031     ShapeEditor *shape_editor = get_current_shape_editor();
1032     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1035 void
1036 sp_node_path_edit_tocurve(void)
1038     ShapeEditor *shape_editor = get_current_shape_editor();
1039     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1042 void
1043 sp_node_path_edit_cusp(void)
1045     ShapeEditor *shape_editor = get_current_shape_editor();
1046     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1049 void
1050 sp_node_path_edit_smooth(void)
1052     ShapeEditor *shape_editor = get_current_shape_editor();
1053     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1056 void
1057 sp_node_path_edit_symmetrical(void)
1059     ShapeEditor *shape_editor = get_current_shape_editor();
1060     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1063 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1064         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1065     bool show = gtk_toggle_action_get_active( act );
1066     prefs->setBool("tools.nodes", "show_handles",  show);
1067     ShapeEditor *shape_editor = get_current_shape_editor();
1068     if (shape_editor) shape_editor->show_handles(show);
1071 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1072     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1073     bool show = gtk_toggle_action_get_active( act );
1074     prefs->setBool("tools.nodes", "show_helperpath",  show);
1075     ShapeEditor *shape_editor = get_current_shape_editor();
1076     if (shape_editor) shape_editor->show_helperpath(show);
1079 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1080     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1083 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1084     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1087 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1088     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1091 /* is called when the node selection is modified */
1092 static void
1093 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1095     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1096     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1097     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1098     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1100     // quit if run by the attr_changed listener
1101     if (g_object_get_data( tbl, "freeze" )) {
1102         return;
1103     }
1105     // in turn, prevent listener from responding
1106     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1108     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1109     SPUnit const *unit = tracker->getActiveUnit();
1111     ShapeEditor *shape_editor = get_current_shape_editor();
1112     if (shape_editor && shape_editor->has_nodepath()) {
1113         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1114         int n_selected = 0;
1115         if (nodepath) {
1116             n_selected = nodepath->numSelected();
1117         }
1119         if (n_selected == 0) {
1120             gtk_action_set_sensitive(xact, FALSE);
1121             gtk_action_set_sensitive(yact, FALSE);
1122         } else {
1123             gtk_action_set_sensitive(xact, TRUE);
1124             gtk_action_set_sensitive(yact, TRUE);
1125             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1126             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1128             if (n_selected == 1) {
1129                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1130                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1131                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1132                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1133                 }
1134             } else {
1135                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1136                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1137                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1138                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1139                        selected nodes don't coincide (in this case we use the coordinates of the center
1140                        of the bounding box). So the entries are never set to zero. */
1141                     // FIXME: Maybe we should clear the entry if several nodes are selected
1142                     //        instead of providing a kind of average value
1143                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1144                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1145                 }
1146             }
1147         }
1148     } else {
1149         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1150         gtk_action_set_sensitive(xact, FALSE);
1151         gtk_action_set_sensitive(yact, FALSE);
1152     }
1154     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1157 static void
1158 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1160     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1161     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1163     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1164     SPUnit const *unit = tracker->getActiveUnit();
1166     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1167         prefs->setDouble("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1168     }
1170     // quit if run by the attr_changed listener
1171     if (g_object_get_data( tbl, "freeze" )) {
1172         return;
1173     }
1175     // in turn, prevent listener from responding
1176     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1178     ShapeEditor *shape_editor = get_current_shape_editor();
1179     if (shape_editor && shape_editor->has_nodepath()) {
1180         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1181         if (!strcmp(value_name, "x")) {
1182             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1183         }
1184         if (!strcmp(value_name, "y")) {
1185             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1186         }
1187     }
1189     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1192 static void
1193 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1195     sp_node_path_value_changed(adj, tbl, "x");
1198 static void
1199 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1201     sp_node_path_value_changed(adj, tbl, "y");
1204 void
1205 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1207     {
1208     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1209     SPItem *item = selection->singleItem();
1210     if (item && SP_IS_LPE_ITEM(item)) {
1211        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1212            gtk_action_set_sensitive(w, TRUE);
1213        } else {
1214            gtk_action_set_sensitive(w, FALSE);
1215        }
1216     } else {
1217        gtk_action_set_sensitive(w, FALSE);
1218     }
1219     }
1221     {
1222     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1223     SPItem *item = selection->singleItem();
1224     if (item && item->clip_ref && item->clip_ref->getObject()) {
1225        gtk_action_set_sensitive(w, TRUE);
1226     } else {
1227        gtk_action_set_sensitive(w, FALSE);
1228     }
1229     }
1231     {
1232     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1233     SPItem *item = selection->singleItem();
1234     if (item && item->mask_ref && item->mask_ref->getObject()) {
1235        gtk_action_set_sensitive(w, TRUE);
1236     } else {
1237        gtk_action_set_sensitive(w, FALSE);
1238     }
1239     }
1242 void
1243 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1245     sp_node_toolbox_sel_changed (selection, tbl);
1250 //################################
1251 //##    Node Editing Toolbox    ##
1252 //################################
1254 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1256         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1257     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1258     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1259     g_object_set_data( holder, "tracker", tracker );
1261     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1263     {
1264         InkAction* inky = ink_action_new( "NodeInsertAction",
1265                                           _("Insert node"),
1266                                           _("Insert new nodes into selected segments"),
1267                                           "node_insert",
1268                                           secondarySize );
1269         g_object_set( inky, "short_label", _("Insert"), NULL );
1270         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1271         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1272     }
1274     {
1275         InkAction* inky = ink_action_new( "NodeDeleteAction",
1276                                           _("Delete node"),
1277                                           _("Delete selected nodes"),
1278                                           "node_delete",
1279                                           secondarySize );
1280         g_object_set( inky, "short_label", _("Delete"), NULL );
1281         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1282         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1283     }
1285     {
1286         InkAction* inky = ink_action_new( "NodeJoinAction",
1287                                           _("Join endnodes"),
1288                                           _("Join selected endnodes"),
1289                                           "node_join",
1290                                           secondarySize );
1291         g_object_set( inky, "short_label", _("Join"), NULL );
1292         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1293         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1294     }
1296     {
1297         InkAction* inky = ink_action_new( "NodeBreakAction",
1298                                           _("Break nodes"),
1299                                           _("Break path at selected nodes"),
1300                                           "node_break",
1301                                           secondarySize );
1302         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1303         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1304     }
1307     {
1308         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1309                                           _("Join with segment"),
1310                                           _("Join selected endnodes with a new segment"),
1311                                           "node_join_segment",
1312                                           secondarySize );
1313         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1314         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1315     }
1317     {
1318         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1319                                           _("Delete segment"),
1320                                           _("Delete segment between two non-endpoint nodes"),
1321                                           "node_delete_segment",
1322                                           secondarySize );
1323         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1324         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1325     }
1327     {
1328         InkAction* inky = ink_action_new( "NodeCuspAction",
1329                                           _("Node Cusp"),
1330                                           _("Make selected nodes corner"),
1331                                           "node_cusp",
1332                                           secondarySize );
1333         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1334         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1335     }
1337     {
1338         InkAction* inky = ink_action_new( "NodeSmoothAction",
1339                                           _("Node Smooth"),
1340                                           _("Make selected nodes smooth"),
1341                                           "node_smooth",
1342                                           secondarySize );
1343         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1344         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1345     }
1347     {
1348         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1349                                           _("Node Symmetric"),
1350                                           _("Make selected nodes symmetric"),
1351                                           "node_symmetric",
1352                                           secondarySize );
1353         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1354         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1355     }
1357     {
1358         InkAction* inky = ink_action_new( "NodeLineAction",
1359                                           _("Node Line"),
1360                                           _("Make selected segments lines"),
1361                                           "node_line",
1362                                           secondarySize );
1363         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1364         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1365     }
1367     {
1368         InkAction* inky = ink_action_new( "NodeCurveAction",
1369                                           _("Node Curve"),
1370                                           _("Make selected segments curves"),
1371                                           "node_curve",
1372                                           secondarySize );
1373         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1374         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375     }
1377     {
1378         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1379                                                       _("Show Handles"),
1380                                                       _("Show the Bezier handles of selected nodes"),
1381                                                       "nodes_show_handles",
1382                                                       Inkscape::ICON_SIZE_DECORATION );
1383         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1384         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1385         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.nodes", "show_handles", true) );
1386     }
1388     {
1389         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1390                                                       _("Show Outline"),
1391                                                       _("Show the outline of the path"),
1392                                                       "nodes_show_helperpath",
1393                                                       Inkscape::ICON_SIZE_DECORATION );
1394         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1395         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1396         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.nodes", "show_helperpath", false) );
1397     }
1399     {
1400         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1401                                           _("Next path effect parameter"),
1402                                           _("Show next path effect parameter for editing"),
1403                                           "edit_next_parameter",
1404                                           Inkscape::ICON_SIZE_DECORATION );
1405         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1406         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1407         g_object_set_data( holder, "nodes_lpeedit", inky);
1408     }
1410     {
1411         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1412                                           _("Edit clipping path"),
1413                                           _("Edit the clipping path of the object"),
1414                                           "nodeedit-clippath",
1415                                           Inkscape::ICON_SIZE_DECORATION );
1416         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418         g_object_set_data( holder, "nodes_clippathedit", inky);
1419     }
1421     {
1422         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1423                                           _("Edit mask path"),
1424                                           _("Edit the mask of the object"),
1425                                           "nodeedit-mask",
1426                                           Inkscape::ICON_SIZE_DECORATION );
1427         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429         g_object_set_data( holder, "nodes_maskedit", inky);
1430     }
1432     /* X coord of selected node(s) */
1433     {
1434         EgeAdjustmentAction* eact = 0;
1435         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1436         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1437         eact = create_adjustment_action( "NodeXAction",
1438                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1439                                          "tools.nodes", "Xcoord", 0,
1440                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1441                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1442                                          labels, values, G_N_ELEMENTS(labels),
1443                                          sp_node_path_x_value_changed );
1444         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1445         g_object_set_data( holder, "nodes_x_action", eact );
1446         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1447         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1448     }
1450     /* Y coord of selected node(s) */
1451     {
1452         EgeAdjustmentAction* eact = 0;
1453         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1454         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1455         eact = create_adjustment_action( "NodeYAction",
1456                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1457                                          "tools.nodes", "Ycoord", 0,
1458                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1459                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1460                                          labels, values, G_N_ELEMENTS(labels),
1461                                          sp_node_path_y_value_changed );
1462         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1463         g_object_set_data( holder, "nodes_y_action", eact );
1464         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1465         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1466     }
1468     // add the units menu
1469     {
1470         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1471         gtk_action_group_add_action( mainActions, act );
1472     }
1475     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1477     //watch selection
1478     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1480     sigc::connection *c_selection_changed =
1481         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1482                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1483     pool->add_connection ("selection-changed", c_selection_changed);
1485     sigc::connection *c_selection_modified =
1486         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1487                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1488     pool->add_connection ("selection-modified", c_selection_modified);
1490     sigc::connection *c_subselection_changed =
1491         new sigc::connection (desktop->connectToolSubselectionChanged
1492                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1493     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1495     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1497     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1498 } // end of sp_node_toolbox_prep()
1501 //########################
1502 //##    Zoom Toolbox    ##
1503 //########################
1505 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1507     // no custom GtkAction setup needed
1508 } // end of sp_zoom_toolbox_prep()
1510 void
1511 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1513     toolbox_set_desktop(toolbox,
1514                         desktop,
1515                         setup_tool_toolbox,
1516                         update_tool_toolbox,
1517                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1518                                                                          "event_context_connection")));
1522 void
1523 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1525     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1526                         desktop,
1527                         setup_aux_toolbox,
1528                         update_aux_toolbox,
1529                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1530                                                                          "event_context_connection")));
1533 void
1534 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1536     toolbox_set_desktop(toolbox,
1537                         desktop,
1538                         setup_commands_toolbox,
1539                         update_commands_toolbox,
1540                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1541                                                                          "event_context_connection")));
1544 static void
1545 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1547     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1548     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1550     if (old_desktop) {
1551         GList *children, *iter;
1553         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1554         for ( iter = children ; iter ; iter = iter->next ) {
1555             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1556         }
1557         g_list_free(children);
1558     }
1560     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1562     if (desktop) {
1563         gtk_widget_set_sensitive(toolbox, TRUE);
1564         setup_func(toolbox, desktop);
1565         update_func(desktop, desktop->event_context, toolbox);
1566         *conn = desktop->connectEventContextChanged
1567             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1568     } else {
1569         gtk_widget_set_sensitive(toolbox, FALSE);
1570     }
1572 } // end of toolbox_set_desktop()
1575 static void
1576 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1578     gchar const * descr =
1579         "<ui>"
1580         "  <toolbar name='ToolToolbar'>"
1581         "    <toolitem action='ToolSelector' />"
1582         "    <toolitem action='ToolNode' />"
1583         "    <toolitem action='ToolTweak' />"
1584         "    <toolitem action='ToolZoom' />"
1585         "    <toolitem action='ToolRect' />"
1586         "    <toolitem action='Tool3DBox' />"
1587         "    <toolitem action='ToolArc' />"
1588         "    <toolitem action='ToolStar' />"
1589         "    <toolitem action='ToolSpiral' />"
1590         "    <toolitem action='ToolPencil' />"
1591         "    <toolitem action='ToolPen' />"
1592         "    <toolitem action='ToolCalligraphic' />"
1593         "    <toolitem action='ToolEraser' />"
1594 //        "    <toolitem action='ToolLPETool' />"
1595         "    <toolitem action='ToolPaintBucket' />"
1596         "    <toolitem action='ToolText' />"
1597         "    <toolitem action='ToolConnector' />"
1598         "    <toolitem action='ToolGradient' />"
1599         "    <toolitem action='ToolDropper' />"
1600         "  </toolbar>"
1601         "</ui>";
1602     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1603     GtkUIManager* mgr = gtk_ui_manager_new();
1604     GError* errVal = 0;
1605     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1607     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1608     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1610     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1611     if ( prefs->getIntLimited("toolbox", "icononly", 1, 0, 1) ) {
1612         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1613     }
1614     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1615     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1617     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1618     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1620     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1622     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1623     if ( child ) {
1624         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1625     }
1627     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1628 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1632 static void
1633 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1635     gchar const *const tname = ( eventcontext
1636                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1637                                  : NULL );
1638     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1640     for (int i = 0 ; tools[i].type_name ; i++ ) {
1641         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1642         if ( act ) {
1643             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1644             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1645             if ( verbAct ) {
1646                 verbAct->set_active(setActive);
1647             }
1648         }
1649     }
1652 static void
1653 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1655         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1656     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1657     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1658     GtkUIManager* mgr = gtk_ui_manager_new();
1659     GError* errVal = 0;
1660     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1661     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1663     std::map<std::string, GtkWidget*> dataHolders;
1665     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1666         if ( aux_toolboxes[i].prep_func ) {
1667             // converted to GtkActions and UIManager
1669             GtkWidget* kludge = gtk_toolbar_new();
1670             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1671             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1672             dataHolders[aux_toolboxes[i].type_name] = kludge;
1673             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1674         } else {
1676             GtkWidget *sub_toolbox = 0;
1677             if (aux_toolboxes[i].create_func == NULL)
1678                 sub_toolbox = sp_empty_toolbox_new(desktop);
1679             else {
1680                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1681             }
1683             gtk_size_group_add_widget( grouper, sub_toolbox );
1685             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1686             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1688         }
1689     }
1691     // Second pass to create toolbars *after* all GtkActions are created
1692     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1693         if ( aux_toolboxes[i].prep_func ) {
1694             // converted to GtkActions and UIManager
1696             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1698             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1699             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1701             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1702             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1703             g_free( tmp );
1704             tmp = 0;
1706             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1707             /// @todo convert to getBool
1708             if ( prefs->getIntLimited( "toolbox", "icononly", 1, 0, 1 ) ) {
1709                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1710             }
1711             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1714             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1716             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1717                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1718                 swatch->setDesktop( desktop );
1719                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1720                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1721                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1722                 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 );
1723             }
1725             gtk_widget_show_all( holder );
1726             sp_set_font_size_smaller( holder );
1728             gtk_size_group_add_widget( grouper, holder );
1730             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1731             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1732         }
1733     }
1735     g_object_unref( G_OBJECT(grouper) );
1738 static void
1739 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1741     gchar const *tname = ( eventcontext
1742                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1743                            : NULL );
1744     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1745         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1746         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1747             gtk_widget_show_all(sub_toolbox);
1748             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1749         } else {
1750             gtk_widget_hide(sub_toolbox);
1751         }
1752     }
1755 static void
1756 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1758     gchar const * descr =
1759         "<ui>"
1760         "  <toolbar name='CommandsToolbar'>"
1761         "    <toolitem action='FileNew' />"
1762         "    <toolitem action='FileOpen' />"
1763         "    <toolitem action='FileSave' />"
1764         "    <toolitem action='FilePrint' />"
1765         "    <separator />"
1766         "    <toolitem action='FileImport' />"
1767         "    <toolitem action='FileExport' />"
1768         "    <separator />"
1769         "    <toolitem action='EditUndo' />"
1770         "    <toolitem action='EditRedo' />"
1771         "    <separator />"
1772         "    <toolitem action='EditCopy' />"
1773         "    <toolitem action='EditCut' />"
1774         "    <toolitem action='EditPaste' />"
1775         "    <separator />"
1776         "    <toolitem action='ZoomSelection' />"
1777         "    <toolitem action='ZoomDrawing' />"
1778         "    <toolitem action='ZoomPage' />"
1779         "    <separator />"
1780         "    <toolitem action='EditDuplicate' />"
1781         "    <toolitem action='EditClone' />"
1782         "    <toolitem action='EditUnlinkClone' />"
1783         "    <separator />"
1784         "    <toolitem action='SelectionGroup' />"
1785         "    <toolitem action='SelectionUnGroup' />"
1786         "    <separator />"
1787         "    <toolitem action='DialogFillStroke' />"
1788         "    <toolitem action='DialogText' />"
1789         "    <toolitem action='DialogXMLEditor' />"
1790         "    <toolitem action='DialogAlignDistribute' />"
1791         "    <separator />"
1792         "    <toolitem action='DialogPreferences' />"
1793         "    <toolitem action='DialogDocumentProperties' />"
1794         "  </toolbar>"
1795         "</ui>";
1796     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1797     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1799     GtkUIManager* mgr = gtk_ui_manager_new();
1800     GError* errVal = 0;
1802     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1803     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1805     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1806     if ( prefs->getBool("toolbox", "icononly", true) ) {
1807         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1808     }
1810     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1811     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1813     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1814     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1817     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1819     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1820     if ( child ) {
1821         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1822     }
1824     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1827 static void
1828 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1832 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1834     gtk_widget_show(toolbox_toplevel);
1835     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1837     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1838     if (!shown_toolbox) {
1839         return;
1840     }
1841     gtk_widget_show(toolbox);
1843     gtk_widget_show_all(shown_toolbox);
1846 static GtkWidget *
1847 sp_empty_toolbox_new(SPDesktop *desktop)
1849     GtkWidget *tbl = gtk_toolbar_new();
1850     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1851     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1853     gtk_widget_show_all(tbl);
1854     sp_set_font_size_smaller (tbl);
1856     return tbl;
1859 #define MODE_LABEL_WIDTH 70
1861 //########################
1862 //##       Star         ##
1863 //########################
1865 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1867     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1869     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1870         // do not remember prefs if this call is initiated by an undo change, because undoing object
1871         // creation sets bogus values to its attributes before it is deleted
1872         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1873         prefs->setInt("tools.shapes.star", "magnitude", (gint)adj->value);
1874     }
1876     // quit if run by the attr_changed listener
1877     if (g_object_get_data( dataKludge, "freeze" )) {
1878         return;
1879     }
1881     // in turn, prevent listener from responding
1882     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1884     bool modmade = false;
1886     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1887     GSList const *items = selection->itemList();
1888     for (; items != NULL; items = items->next) {
1889         if (SP_IS_STAR((SPItem *) items->data)) {
1890             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1891             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1892             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1893                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1894                                     + M_PI / (gint)adj->value));
1895             SP_OBJECT((SPItem *) items->data)->updateRepr();
1896             modmade = true;
1897         }
1898     }
1899     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1900                                    _("Star: Change number of corners"));
1902     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1905 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1907     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1909     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1910         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1911         prefs->setDouble("tools.shapes.star", "proportion", adj->value);
1912     }
1914     // quit if run by the attr_changed listener
1915     if (g_object_get_data( dataKludge, "freeze" )) {
1916         return;
1917     }
1919     // in turn, prevent listener from responding
1920     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1922     bool modmade = false;
1923     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1924     GSList const *items = selection->itemList();
1925     for (; items != NULL; items = items->next) {
1926         if (SP_IS_STAR((SPItem *) items->data)) {
1927             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1929             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1930             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1931             if (r2 < r1) {
1932                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1933             } else {
1934                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1935             }
1937             SP_OBJECT((SPItem *) items->data)->updateRepr();
1938             modmade = true;
1939         }
1940     }
1942     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1943                                    _("Star: Change spoke ratio"));
1945     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1948 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1950     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1951     bool flat = ege_select_one_action_get_active( act ) == 0;
1953     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1954         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1955         prefs->setBool( "tools.shapes.star", "isflatsided", flat);
1956     }
1958     // quit if run by the attr_changed listener
1959     if (g_object_get_data( dataKludge, "freeze" )) {
1960         return;
1961     }
1963     // in turn, prevent listener from responding
1964     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1966     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1967     GSList const *items = selection->itemList();
1968     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1969     bool modmade = false;
1971     if ( prop_action ) {
1972         gtk_action_set_sensitive( prop_action, !flat );
1973     }
1975     for (; items != NULL; items = items->next) {
1976         if (SP_IS_STAR((SPItem *) items->data)) {
1977             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1978             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1979             SP_OBJECT((SPItem *) items->data)->updateRepr();
1980             modmade = true;
1981         }
1982     }
1984     if (modmade) {
1985         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1986                          flat ? _("Make polygon") : _("Make star"));
1987     }
1989     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1992 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1994     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1996     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1997         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1998         prefs->setDouble("tools.shapes.star", "rounded", (gdouble) adj->value);
1999     }
2001     // quit if run by the attr_changed listener
2002     if (g_object_get_data( dataKludge, "freeze" )) {
2003         return;
2004     }
2006     // in turn, prevent listener from responding
2007     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2009     bool modmade = false;
2011     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2012     GSList const *items = selection->itemList();
2013     for (; items != NULL; items = items->next) {
2014         if (SP_IS_STAR((SPItem *) items->data)) {
2015             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2016             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2017             SP_OBJECT(items->data)->updateRepr();
2018             modmade = true;
2019         }
2020     }
2021     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2022                                    _("Star: Change rounding"));
2024     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2027 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2029     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2031     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2032         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2033         prefs->setDouble("tools.shapes.star", "randomized", (gdouble) adj->value);
2034     }
2036     // quit if run by the attr_changed listener
2037     if (g_object_get_data( dataKludge, "freeze" )) {
2038         return;
2039     }
2041     // in turn, prevent listener from responding
2042     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2044     bool modmade = false;
2046     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2047     GSList const *items = selection->itemList();
2048     for (; items != NULL; items = items->next) {
2049         if (SP_IS_STAR((SPItem *) items->data)) {
2050             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2051             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2052             SP_OBJECT(items->data)->updateRepr();
2053             modmade = true;
2054         }
2055     }
2056     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2057                                    _("Star: Change randomization"));
2059     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2063 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2064                                        gchar const */*old_value*/, gchar const */*new_value*/,
2065                                        bool /*is_interactive*/, gpointer data)
2067     GtkWidget *tbl = GTK_WIDGET(data);
2069     // quit if run by the _changed callbacks
2070     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2071         return;
2072     }
2074     // in turn, prevent callbacks from responding
2075     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2077     GtkAdjustment *adj = 0;
2079         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2080     bool isFlatSided = prefs->getBool("tools.shapes.star", "isflatsided", true);
2082     if (!strcmp(name, "inkscape:randomized")) {
2083         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2084         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2085     } else if (!strcmp(name, "inkscape:rounded")) {
2086         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2087         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2088     } else if (!strcmp(name, "inkscape:flatsided")) {
2089         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2090         char const *flatsides = repr->attribute("inkscape:flatsided");
2091         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2092         if ( flatsides && !strcmp(flatsides,"false") ) {
2093             ege_select_one_action_set_active( flat_action, 1 );
2094             gtk_action_set_sensitive( prop_action, TRUE );
2095         } else {
2096             ege_select_one_action_set_active( flat_action, 0 );
2097             gtk_action_set_sensitive( prop_action, FALSE );
2098         }
2099     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2100         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2101         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2102         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2103         if (r2 < r1) {
2104             gtk_adjustment_set_value(adj, r2/r1);
2105         } else {
2106             gtk_adjustment_set_value(adj, r1/r2);
2107         }
2108     } else if (!strcmp(name, "sodipodi:sides")) {
2109         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2110         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2111     }
2113     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2117 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2119     NULL, /* child_added */
2120     NULL, /* child_removed */
2121     star_tb_event_attr_changed,
2122     NULL, /* content_changed */
2123     NULL  /* order_changed */
2124 };
2127 /**
2128  *  \param selection Should not be NULL.
2129  */
2130 static void
2131 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2133     int n_selected = 0;
2134     Inkscape::XML::Node *repr = NULL;
2136     purge_repr_listener( tbl, tbl );
2138     for (GSList const *items = selection->itemList();
2139          items != NULL;
2140          items = items->next)
2141     {
2142         if (SP_IS_STAR((SPItem *) items->data)) {
2143             n_selected++;
2144             repr = SP_OBJECT_REPR((SPItem *) items->data);
2145         }
2146     }
2148     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2150     if (n_selected == 0) {
2151         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2152     } else if (n_selected == 1) {
2153         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2155         if (repr) {
2156             g_object_set_data( tbl, "repr", repr );
2157             Inkscape::GC::anchor(repr);
2158             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2159             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2160         }
2161     } else {
2162         // FIXME: implement averaging of all parameters for multiple selected stars
2163         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2164         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2165     }
2169 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2171     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2172     // callbacks to lump all the changes for all selected objects in one undo step
2174     GtkAdjustment *adj = 0;
2176     // fixme: make settable in prefs!
2177     gint mag = 5;
2178     gdouble prop = 0.5;
2179     gboolean flat = FALSE;
2180     gdouble randomized = 0;
2181     gdouble rounded = 0;
2183     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2184     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2186     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2187     gtk_action_set_sensitive( sb2, !flat );
2189     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2190     gtk_adjustment_set_value(adj, mag);
2191     gtk_adjustment_value_changed(adj);
2193     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2194     gtk_adjustment_set_value(adj, prop);
2195     gtk_adjustment_value_changed(adj);
2197     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2198     gtk_adjustment_set_value(adj, rounded);
2199     gtk_adjustment_value_changed(adj);
2201     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2202     gtk_adjustment_set_value(adj, randomized);
2203     gtk_adjustment_value_changed(adj);
2207 void
2208 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2210     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2211     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2212     GtkWidget *l = gtk_label_new(NULL);
2213     gtk_label_set_markup(GTK_LABEL(l), title);
2214     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2215     if ( GTK_IS_TOOLBAR(tbl) ) {
2216         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2217     } else {
2218         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2219     }
2220     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2224 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2226     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2228     {
2229         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2230         ege_output_action_set_use_markup( act, TRUE );
2231         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2232         g_object_set_data( holder, "mode_action", act );
2233     }
2235     {
2236         EgeAdjustmentAction* eact = 0;
2237         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2238         bool isFlatSided = prefs->getBool("tools.shapes.star", "isflatsided", true);
2240         /* Flatsided checkbox */
2241         {
2242             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2244             GtkTreeIter iter;
2245             gtk_list_store_append( model, &iter );
2246             gtk_list_store_set( model, &iter,
2247                                 0, _("Polygon"),
2248                                 1, _("Regular polygon (with one handle) instead of a star"),
2249                                 2, "star_flat",
2250                                 -1 );
2252             gtk_list_store_append( model, &iter );
2253             gtk_list_store_set( model, &iter,
2254                                 0, _("Star"),
2255                                 1, _("Star instead of a regular polygon (with one handle)"),
2256                                 2, "star_angled",
2257                                 -1 );
2259             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2260             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2261             g_object_set_data( holder, "flat_action", act );
2263             ege_select_one_action_set_appearance( act, "full" );
2264             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2265             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2266             ege_select_one_action_set_icon_column( act, 2 );
2267             ege_select_one_action_set_icon_size( act, secondarySize );
2268             ege_select_one_action_set_tooltip_column( act, 1  );
2270             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2271             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2272         }
2274         /* Magnitude */
2275         {
2276         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2277         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2278         eact = create_adjustment_action( "MagnitudeAction",
2279                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2280                                          "tools.shapes.star", "magnitude", 3,
2281                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2282                                          3, 1024, 1, 5,
2283                                          labels, values, G_N_ELEMENTS(labels),
2284                                          sp_stb_magnitude_value_changed,
2285                                          1.0, 0 );
2286         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2287         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2288         }
2290         /* Spoke ratio */
2291         {
2292         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2293         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2294         eact = create_adjustment_action( "SpokeAction",
2295                                          _("Spoke ratio"), _("Spoke ratio:"),
2296                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2297                                          // Base radius is the same for the closest handle.
2298                                          _("Base radius to tip radius ratio"),
2299                                          "tools.shapes.star", "proportion", 0.5,
2300                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2301                                          0.01, 1.0, 0.01, 0.1,
2302                                          labels, values, G_N_ELEMENTS(labels),
2303                                          sp_stb_proportion_value_changed );
2304         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2305         g_object_set_data( holder, "prop_action", eact );
2306         }
2308         if ( !isFlatSided ) {
2309             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2310         } else {
2311             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2312         }
2314         /* Roundedness */
2315         {
2316         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2317         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2318         eact = create_adjustment_action( "RoundednessAction",
2319                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2320                                          "tools.shapes.star", "rounded", 0.0,
2321                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2322                                          -10.0, 10.0, 0.01, 0.1,
2323                                          labels, values, G_N_ELEMENTS(labels),
2324                                          sp_stb_rounded_value_changed );
2325         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2326         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2327         }
2329         /* Randomization */
2330         {
2331         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2332         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2333         eact = create_adjustment_action( "RandomizationAction",
2334                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2335                                          "tools.shapes.star", "randomized", 0.0,
2336                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2337                                          -10.0, 10.0, 0.001, 0.01,
2338                                          labels, values, G_N_ELEMENTS(labels),
2339                                          sp_stb_randomized_value_changed, 0.1, 3 );
2340         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2341         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2342         }
2343     }
2345     {
2346         /* Reset */
2347         {
2348             GtkAction* act = gtk_action_new( "StarResetAction",
2349                                              _("Defaults"),
2350                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2351                                              GTK_STOCK_CLEAR );
2352             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2353             gtk_action_group_add_action( mainActions, act );
2354             gtk_action_set_sensitive( act, TRUE );
2355         }
2356     }
2358     sigc::connection *connection = new sigc::connection(
2359         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2360         );
2361     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2362     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2366 //########################
2367 //##       Rect         ##
2368 //########################
2370 static void sp_rtb_sensitivize( GObject *tbl )
2372     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2373     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2374     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2376     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2377         gtk_action_set_sensitive( not_rounded, FALSE );
2378     } else {
2379         gtk_action_set_sensitive( not_rounded, TRUE );
2380     }
2384 static void
2385 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2386                           void (*setter)(SPRect *, gdouble))
2388     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2390     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2391     SPUnit const *unit = tracker->getActiveUnit();
2393     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2394         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2395         prefs->setDouble("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2396     }
2398     // quit if run by the attr_changed listener
2399     if (g_object_get_data( tbl, "freeze" )) {
2400         return;
2401     }
2403     // in turn, prevent listener from responding
2404     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2406     bool modmade = false;
2407     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2408     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2409         if (SP_IS_RECT(items->data)) {
2410             if (adj->value != 0) {
2411                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2412             } else {
2413                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2414             }
2415             modmade = true;
2416         }
2417     }
2419     sp_rtb_sensitivize( tbl );
2421     if (modmade) {
2422         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2423                                    _("Change rectangle"));
2424     }
2426     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2429 static void
2430 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2432     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2435 static void
2436 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2438     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2441 static void
2442 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2444     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2447 static void
2448 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2450     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2455 static void
2456 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2458     GtkAdjustment *adj = 0;
2460     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2461     gtk_adjustment_set_value(adj, 0.0);
2462     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2463     gtk_adjustment_value_changed(adj);
2465     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2466     gtk_adjustment_set_value(adj, 0.0);
2467     gtk_adjustment_value_changed(adj);
2469     sp_rtb_sensitivize( obj );
2472 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2473                                        gchar const */*old_value*/, gchar const */*new_value*/,
2474                                        bool /*is_interactive*/, gpointer data)
2476     GObject *tbl = G_OBJECT(data);
2478     // quit if run by the _changed callbacks
2479     if (g_object_get_data( tbl, "freeze" )) {
2480         return;
2481     }
2483     // in turn, prevent callbacks from responding
2484     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2486     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2487     SPUnit const *unit = tracker->getActiveUnit();
2489     gpointer item = g_object_get_data( tbl, "item" );
2490     if (item && SP_IS_RECT(item)) {
2491         {
2492             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2493             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2494             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2495         }
2497         {
2498             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2499             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2500             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2501         }
2503         {
2504             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2505             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2506             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2507         }
2509         {
2510             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2511             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2512             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2513         }
2514     }
2516     sp_rtb_sensitivize( tbl );
2518     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2522 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2523     NULL, /* child_added */
2524     NULL, /* child_removed */
2525     rect_tb_event_attr_changed,
2526     NULL, /* content_changed */
2527     NULL  /* order_changed */
2528 };
2530 /**
2531  *  \param selection should not be NULL.
2532  */
2533 static void
2534 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2536     int n_selected = 0;
2537     Inkscape::XML::Node *repr = NULL;
2538     SPItem *item = NULL;
2540     if ( g_object_get_data( tbl, "repr" ) ) {
2541         g_object_set_data( tbl, "item", NULL );
2542     }
2543     purge_repr_listener( tbl, tbl );
2545     for (GSList const *items = selection->itemList();
2546          items != NULL;
2547          items = items->next) {
2548         if (SP_IS_RECT((SPItem *) items->data)) {
2549             n_selected++;
2550             item = (SPItem *) items->data;
2551             repr = SP_OBJECT_REPR(item);
2552         }
2553     }
2555     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2557     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2559     if (n_selected == 0) {
2560         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2562         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2563         gtk_action_set_sensitive(w, FALSE);
2564         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2565         gtk_action_set_sensitive(h, FALSE);
2567     } else if (n_selected == 1) {
2568         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2569         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2571         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2572         gtk_action_set_sensitive(w, TRUE);
2573         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2574         gtk_action_set_sensitive(h, TRUE);
2576         if (repr) {
2577             g_object_set_data( tbl, "repr", repr );
2578             g_object_set_data( tbl, "item", item );
2579             Inkscape::GC::anchor(repr);
2580             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2581             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2582         }
2583     } else {
2584         // FIXME: implement averaging of all parameters for multiple selected
2585         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2586         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2587         sp_rtb_sensitivize( tbl );
2588     }
2592 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2594     EgeAdjustmentAction* eact = 0;
2595     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2597     {
2598         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2599         ege_output_action_set_use_markup( act, TRUE );
2600         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2601         g_object_set_data( holder, "mode_action", act );
2602     }
2604     // rx/ry units menu: create
2605     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2606     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2607     // fixme: add % meaning per cent of the width/height
2608     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2609     g_object_set_data( holder, "tracker", tracker );
2611     /* W */
2612     {
2613         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2614         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2615         eact = create_adjustment_action( "RectWidthAction",
2616                                          _("Width"), _("W:"), _("Width of rectangle"),
2617                                          "tools.shapes.rect", "width", 0,
2618                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2619                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2620                                          labels, values, G_N_ELEMENTS(labels),
2621                                          sp_rtb_width_value_changed );
2622         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2623         g_object_set_data( holder, "width_action", eact );
2624         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2625         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2626     }
2628     /* H */
2629     {
2630         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2631         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2632         eact = create_adjustment_action( "RectHeightAction",
2633                                          _("Height"), _("H:"), _("Height of rectangle"),
2634                                          "tools.shapes.rect", "height", 0,
2635                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2636                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2637                                          labels, values, G_N_ELEMENTS(labels),
2638                                          sp_rtb_height_value_changed );
2639         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2640         g_object_set_data( holder, "height_action", eact );
2641         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2642         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2643     }
2645     /* rx */
2646     {
2647         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2648         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2649         eact = create_adjustment_action( "RadiusXAction",
2650                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2651                                          "tools.shapes.rect", "rx", 0,
2652                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2653                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2654                                          labels, values, G_N_ELEMENTS(labels),
2655                                          sp_rtb_rx_value_changed);
2656         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2657         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2658     }
2660     /* ry */
2661     {
2662         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2663         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2664         eact = create_adjustment_action( "RadiusYAction",
2665                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2666                                          "tools.shapes.rect", "ry", 0,
2667                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2668                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2669                                          labels, values, G_N_ELEMENTS(labels),
2670                                          sp_rtb_ry_value_changed);
2671         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2672         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2673     }
2675     // add the units menu
2676     {
2677         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2678         gtk_action_group_add_action( mainActions, act );
2679     }
2681     /* Reset */
2682     {
2683         InkAction* inky = ink_action_new( "RectResetAction",
2684                                           _("Not rounded"),
2685                                           _("Make corners sharp"),
2686                                           "squared_corner",
2687                                           secondarySize );
2688         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2689         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2690         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2691         g_object_set_data( holder, "not_rounded", inky );
2692     }
2694     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2695     sp_rtb_sensitivize( holder );
2697     sigc::connection *connection = new sigc::connection(
2698         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2699         );
2700     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2701     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2704 //########################
2705 //##       3D Box       ##
2706 //########################
2708 // normalize angle so that it lies in the interval [0,360]
2709 static double box3d_normalize_angle (double a) {
2710     double angle = a + ((int) (a/360.0))*360;
2711     if (angle < 0) {
2712         angle += 360.0;
2713     }
2714     return angle;
2717 static void
2718 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2719                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2720     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2721     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2722     //       are reset).
2723     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2725     if (is_infinite) {
2726         gtk_toggle_action_set_active(tact, TRUE);
2727         gtk_action_set_sensitive(act, TRUE);
2729         double angle = persp3d_get_infinite_angle(persp, axis);
2730         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2731             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2732         }
2733     } else {
2734         gtk_toggle_action_set_active(tact, FALSE);
2735         gtk_action_set_sensitive(act, FALSE);
2736     }
2739 static void
2740 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2741     if (!persp_repr) {
2742         g_print ("No perspective given to box3d_resync_toolbar().\n");
2743         return;
2744     }
2746     GtkWidget *tbl = GTK_WIDGET(data);
2747     GtkAdjustment *adj = 0;
2748     GtkAction *act = 0;
2749     GtkToggleAction *tact = 0;
2750     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2751     {
2752         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2753         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2754         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2756         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2757     }
2758     {
2759         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2760         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2761         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2763         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2764     }
2765     {
2766         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2767         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2768         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2770         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2771     }
2774 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2775                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2776                                                   bool /*is_interactive*/, gpointer data)
2778     GtkWidget *tbl = GTK_WIDGET(data);
2780     // quit if run by the attr_changed listener
2781     // note: it used to work without the differently called freeze_ attributes (here and in
2782     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2783     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2784         return;
2785     }
2787     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2788     // sp_document_maybe_done() when the document is undo insensitive)
2789     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2791     // TODO: Only update the appropriate part of the toolbar
2792 //    if (!strcmp(name, "inkscape:vp_z")) {
2793         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2794 //    }
2796     Persp3D *persp = persp3d_get_from_repr(repr);
2797     persp3d_update_box_reprs(persp);
2799     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2802 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2804     NULL, /* child_added */
2805     NULL, /* child_removed */
2806     box3d_persp_tb_event_attr_changed,
2807     NULL, /* content_changed */
2808     NULL  /* order_changed */
2809 };
2811 /**
2812  *  \param selection Should not be NULL.
2813  */
2814 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2815 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2816 static void
2817 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2819     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2820     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2821     // update the perspectives with infinite VPs and leave the other ones untouched).
2823     Inkscape::XML::Node *persp_repr = NULL;
2824     purge_repr_listener(tbl, tbl);
2826     SPItem *item = selection->singleItem();
2827     if (item && SP_IS_BOX3D(item)) {
2828         // FIXME: Also deal with multiple selected boxes
2829         SPBox3D *box = SP_BOX3D(item);
2830         Persp3D *persp = box3d_get_perspective(box);
2831         persp_repr = SP_OBJECT_REPR(persp);
2832         if (persp_repr) {
2833             g_object_set_data(tbl, "repr", persp_repr);
2834             Inkscape::GC::anchor(persp_repr);
2835             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2836             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2837         }
2839         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2840         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2841         prefs->setString("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2843         box3d_resync_toolbar(persp_repr, tbl);
2844     }
2847 static void
2848 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2850     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2851     SPDocument *document = sp_desktop_document(desktop);
2853     // quit if run by the attr_changed listener
2854     // note: it used to work without the differently called freeze_ attributes (here and in
2855     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2856     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2857         return;
2858     }
2860     // in turn, prevent listener from responding
2861     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2863     //Persp3D *persp = document->current_persp3d;
2864     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2865     if (sel_persps.empty()) {
2866         // this can happen when the document is created; we silently ignore it
2867         return;
2868     }
2869     Persp3D *persp = sel_persps.front();
2871     persp->tmat.set_infinite_direction (axis, adj->value);
2872     SP_OBJECT(persp)->updateRepr();
2874     // TODO: use the correct axis here, too
2875     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2877     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2881 static void
2882 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2884     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2887 static void
2888 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2890     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2893 static void
2894 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2896     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2900 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2902     // TODO: Take all selected perspectives into account
2903     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2904     if (sel_persps.empty()) {
2905         // this can happen when the document is created; we silently ignore it
2906         return;
2907     }
2908     Persp3D *persp = sel_persps.front();
2910     bool set_infinite = gtk_toggle_action_get_active(act);
2911     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2914 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2916     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2919 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2921     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2924 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2926     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2929 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2931         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2932     EgeAdjustmentAction* eact = 0;
2933     SPDocument *document = sp_desktop_document (desktop);
2934     Persp3D *persp = document->current_persp3d;
2936     EgeAdjustmentAction* box3d_angle_x = 0;
2937     EgeAdjustmentAction* box3d_angle_y = 0;
2938     EgeAdjustmentAction* box3d_angle_z = 0;
2940     /* Angle X */
2941     {
2942         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2943         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2944         eact = create_adjustment_action( "3DBoxAngleXAction",
2945                                          _("Angle in X direction"), _("Angle X:"),
2946                                          // Translators: PL is short for 'perspective line'
2947                                          _("Angle of PLs in X direction"),
2948                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2949                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2950                                          -360.0, 360.0, 1.0, 10.0,
2951                                          labels, values, G_N_ELEMENTS(labels),
2952                                          box3d_angle_x_value_changed );
2953         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2954         g_object_set_data( holder, "box3d_angle_x_action", eact );
2955         box3d_angle_x = eact;
2956     }
2958     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2959         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2960     } else {
2961         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2962     }
2965     /* VP X state */
2966     {
2967         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2968                                                       // Translators: VP is short for 'vanishing point'
2969                                                       _("State of VP in X direction"),
2970                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2971                                                       "toggle_vp_x",
2972                                                       Inkscape::ICON_SIZE_DECORATION );
2973         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2974         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2975         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2976         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("tools.shapes.3dbox", "vp_x_state", true) );
2977         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.shapes.3dbox", "vp_x_state", true) );
2978     }
2980     /* Angle Y */
2981     {
2982         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2983         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2984         eact = create_adjustment_action( "3DBoxAngleYAction",
2985                                          _("Angle in Y direction"), _("Angle Y:"),
2986                                          // Translators: PL is short for 'perspective line'
2987                                          _("Angle of PLs in Y direction"),
2988                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2989                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2990                                          -360.0, 360.0, 1.0, 10.0,
2991                                          labels, values, G_N_ELEMENTS(labels),
2992                                          box3d_angle_y_value_changed );
2993         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2994         g_object_set_data( holder, "box3d_angle_y_action", eact );
2995         box3d_angle_y = eact;
2996     }
2998     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2999         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3000     } else {
3001         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3002     }
3004     /* VP Y state */
3005     {
3006         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3007                                                       // Translators: VP is short for 'vanishing point'
3008                                                       _("State of VP in Y direction"),
3009                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3010                                                       "toggle_vp_y",
3011                                                       Inkscape::ICON_SIZE_DECORATION );
3012         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3013         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3014         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3015         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("tools.shapes.3dbox", "vp_y_state", true) );
3016         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.shapes.3dbox", "vp_y_state", true) );
3017     }
3019     /* Angle Z */
3020     {
3021         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3022         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3023         eact = create_adjustment_action( "3DBoxAngleZAction",
3024                                          _("Angle in Z direction"), _("Angle Z:"),
3025                                          // Translators: PL is short for 'perspective line'
3026                                          _("Angle of PLs in Z direction"),
3027                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
3028                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3029                                          -360.0, 360.0, 1.0, 10.0,
3030                                          labels, values, G_N_ELEMENTS(labels),
3031                                          box3d_angle_z_value_changed );
3032         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3033         g_object_set_data( holder, "box3d_angle_z_action", eact );
3034         box3d_angle_z = eact;
3035     }
3037     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3038         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3039     } else {
3040         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3041     }
3043     /* VP Z state */
3044     {
3045         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3046                                                       // Translators: VP is short for 'vanishing point'
3047                                                       _("State of VP in Z direction"),
3048                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3049                                                       "toggle_vp_z",
3050                                                       Inkscape::ICON_SIZE_DECORATION );
3051         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3052         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3053         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3054         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("tools.shapes.3dbox", "vp_z_state", true) );
3055         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.shapes.3dbox", "vp_z_state", true) );
3056     }
3058     sigc::connection *connection = new sigc::connection(
3059         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3060        );
3061     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3062     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3065 //########################
3066 //##       Spiral       ##
3067 //########################
3069 static void
3070 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3072     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3074     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3075         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3076         prefs->setDouble("tools.shapes.spiral", value_name, adj->value);
3077     }
3079     // quit if run by the attr_changed listener
3080     if (g_object_get_data( tbl, "freeze" )) {
3081         return;
3082     }
3084     // in turn, prevent listener from responding
3085     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3087     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3089     bool modmade = false;
3090     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3091          items != NULL;
3092          items = items->next)
3093     {
3094         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3095             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3096             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3097             SP_OBJECT((SPItem *) items->data)->updateRepr();
3098             modmade = true;
3099         }
3100     }
3102     g_free(namespaced_name);
3104     if (modmade) {
3105         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3106                                    _("Change spiral"));
3107     }
3109     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3112 static void
3113 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3115     sp_spl_tb_value_changed(adj, tbl, "revolution");
3118 static void
3119 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3121     sp_spl_tb_value_changed(adj, tbl, "expansion");
3124 static void
3125 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3127     sp_spl_tb_value_changed(adj, tbl, "t0");
3130 static void
3131 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3133     GtkWidget *tbl = GTK_WIDGET(obj);
3135     GtkAdjustment *adj;
3137     // fixme: make settable
3138     gdouble rev = 5;
3139     gdouble exp = 1.0;
3140     gdouble t0 = 0.0;
3142     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3143     gtk_adjustment_set_value(adj, rev);
3144     gtk_adjustment_value_changed(adj);
3146     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3147     gtk_adjustment_set_value(adj, exp);
3148     gtk_adjustment_value_changed(adj);
3150     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3151     gtk_adjustment_set_value(adj, t0);
3152     gtk_adjustment_value_changed(adj);
3154     spinbutton_defocus(GTK_OBJECT(tbl));
3158 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3159                                          gchar const */*old_value*/, gchar const */*new_value*/,
3160                                          bool /*is_interactive*/, gpointer data)
3162     GtkWidget *tbl = GTK_WIDGET(data);
3164     // quit if run by the _changed callbacks
3165     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3166         return;
3167     }
3169     // in turn, prevent callbacks from responding
3170     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3172     GtkAdjustment *adj;
3173     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3174     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3176     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3177     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3179     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3180     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3182     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3186 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3187     NULL, /* child_added */
3188     NULL, /* child_removed */
3189     spiral_tb_event_attr_changed,
3190     NULL, /* content_changed */
3191     NULL  /* order_changed */
3192 };
3194 static void
3195 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3197     int n_selected = 0;
3198     Inkscape::XML::Node *repr = NULL;
3200     purge_repr_listener( tbl, tbl );
3202     for (GSList const *items = selection->itemList();
3203          items != NULL;
3204          items = items->next)
3205     {
3206         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3207             n_selected++;
3208             repr = SP_OBJECT_REPR((SPItem *) items->data);
3209         }
3210     }
3212     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3214     if (n_selected == 0) {
3215         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3216     } else if (n_selected == 1) {
3217         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3219         if (repr) {
3220             g_object_set_data( tbl, "repr", repr );
3221             Inkscape::GC::anchor(repr);
3222             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3223             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3224         }
3225     } else {
3226         // FIXME: implement averaging of all parameters for multiple selected
3227         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3228         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3229     }
3233 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3235     EgeAdjustmentAction* eact = 0;
3236     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3238     {
3239         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3240         ege_output_action_set_use_markup( act, TRUE );
3241         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3242         g_object_set_data( holder, "mode_action", act );
3243     }
3245     /* Revolution */
3246     {
3247         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3248         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3249         eact = create_adjustment_action( "SpiralRevolutionAction",
3250                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3251                                          "tools.shapes.spiral", "revolution", 3.0,
3252                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3253                                          0.01, 1024.0, 0.1, 1.0,
3254                                          labels, values, G_N_ELEMENTS(labels),
3255                                          sp_spl_tb_revolution_value_changed, 1, 2);
3256         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3257     }
3259     /* Expansion */
3260     {
3261         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3262         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3263         eact = create_adjustment_action( "SpiralExpansionAction",
3264                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3265                                          "tools.shapes.spiral", "expansion", 1.0,
3266                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3267                                          0.0, 1000.0, 0.01, 1.0,
3268                                          labels, values, G_N_ELEMENTS(labels),
3269                                          sp_spl_tb_expansion_value_changed);
3270         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3271     }
3273     /* T0 */
3274     {
3275         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3276         gdouble values[] = {0, 0.5, 0.9};
3277         eact = create_adjustment_action( "SpiralT0Action",
3278                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3279                                          "tools.shapes.spiral", "t0", 0.0,
3280                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3281                                          0.0, 0.999, 0.01, 1.0,
3282                                          labels, values, G_N_ELEMENTS(labels),
3283                                          sp_spl_tb_t0_value_changed);
3284         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3285     }
3287     /* Reset */
3288     {
3289         InkAction* inky = ink_action_new( "SpiralResetAction",
3290                                           _("Defaults"),
3291                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3292                                           GTK_STOCK_CLEAR,
3293                                           secondarySize );
3294         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3295         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3296     }
3299     sigc::connection *connection = new sigc::connection(
3300         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3301         );
3302     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3303     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3306 //########################
3307 //##     Pen/Pencil     ##
3308 //########################
3310 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3311 static char const *
3312 freehand_tool_name(GObject *dataKludge)
3314     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3315     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3316              ? "tools.freehand.pen"
3317              : "tools.freehand.pencil" );
3320 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3322     gint mode = ege_select_one_action_get_active(act);
3324         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3325     prefs->setInt(freehand_tool_name(tbl), "freehand-mode", mode);
3327     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3329     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3330     // preparatory work here
3331     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3332         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3333         sp_pen_context_set_polyline_mode(pc);
3334     }
3337 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3339     /* Freehand mode toggle buttons */
3340     {
3341         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3342         guint freehandMode = prefs->getInt(( tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen" ), "freehand-mode", 0);
3343         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3345         {
3346             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3348             GtkTreeIter iter;
3349             gtk_list_store_append( model, &iter );
3350             gtk_list_store_set( model, &iter,
3351                                 0, _("Bezier"),
3352                                 1, _("Create regular Bezier path"),
3353                                 2, "bezier_mode",
3354                                 -1 );
3356             gtk_list_store_append( model, &iter );
3357             gtk_list_store_set( model, &iter,
3358                                 0, _("Spiro"),
3359                                 1, _("Create Spiro path"),
3360                                 2, "spiro_splines_mode",
3361                                 -1 );
3363             if (!tool_is_pencil) {
3364                 gtk_list_store_append( model, &iter );
3365                 gtk_list_store_set( model, &iter,
3366                                     0, _("Zigzag"),
3367                                     1, _("Create a sequence of straight line segments"),
3368                                     2, "polylines_mode",
3369                                     -1 );
3371                 gtk_list_store_append( model, &iter );
3372                 gtk_list_store_set( model, &iter,
3373                                     0, _("Paraxial"),
3374                                     1, _("Create a sequence of paraxial line segments"),
3375                                     2, "paraxial_lines_mode",
3376                                     -1 );
3377             }
3379             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3380                                                                 "FreehandModeActionPencil" :
3381                                                                 "FreehandModeActionPen",
3382                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3383             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3385             ege_select_one_action_set_appearance( act, "full" );
3386             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3387             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3388             ege_select_one_action_set_icon_column( act, 2 );
3389             ege_select_one_action_set_icon_size( act, secondarySize );
3390             ege_select_one_action_set_tooltip_column( act, 1  );
3392             ege_select_one_action_set_active( act, freehandMode);
3393             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3394         }
3395     }
3398 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3399     gint shape = ege_select_one_action_get_active( act );
3400     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3401     prefs->setInt(freehand_tool_name(dataKludge), "shape", shape);
3404 /**
3405  * \brief Generate the list of freehand advanced shape option entries.
3406  */
3407 GList * freehand_shape_dropdown_items_list() {
3408     GList *glist = NULL;
3410     glist = g_list_append (glist, _("None"));
3411     glist = g_list_append (glist, _("Triangle in"));
3412     glist = g_list_append (glist, _("Triangle out"));
3413     glist = g_list_append (glist, _("Ellipse"));
3414     glist = g_list_append (glist, _("From clipboard"));
3416     return glist;
3419 static void
3420 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3421     /*advanced shape options */
3422     {
3423         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3424         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3426         GList* items = 0;
3427         gint count = 0;
3428         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3429         {
3430             GtkTreeIter iter;
3431             gtk_list_store_append( model, &iter );
3432             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3433             count++;
3434         }
3435         g_list_free( items );
3436         items = 0;
3437         EgeSelectOneAction* act1 = ege_select_one_action_new(
3438             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3439             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3440         g_object_set( act1, "short_label", _("Shape:"), NULL );
3441         ege_select_one_action_set_appearance( act1, "compact" );
3442         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen" ), "shape", 0) );
3443         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3444         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3445         g_object_set_data( holder, "shape_action", act1 );
3446     }
3449 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3451     sp_add_freehand_mode_toggle(mainActions, holder, false);
3452     freehand_add_advanced_shape_options(mainActions, holder, false);
3456 static void
3457 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3459     GtkWidget *tbl = GTK_WIDGET(obj);
3461     GtkAdjustment *adj;
3463     // fixme: make settable
3464     gdouble tolerance = 4;
3466     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3467     gtk_adjustment_set_value(adj, tolerance);
3468     gtk_adjustment_value_changed(adj);
3470     spinbutton_defocus(GTK_OBJECT(tbl));
3473 static void
3474 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3477     // quit if run by the attr_changed listener
3478     if (g_object_get_data( tbl, "freeze" )) {
3479         return;
3480     }
3481     // in turn, prevent listener from responding
3482     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3483     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3484     prefs->setDouble("tools.freehand.pencil", "tolerance", adj->value);
3485     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3491 static void
3492 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3493                                               const gchar */*key*/,
3494                                               const gchar */*oldval*/,
3495                                               const gchar */*newval*/,
3496                                               bool /*is_interactive*/,
3497                                               void * data)
3499     GObject* tbl = G_OBJECT(data);
3500     if (g_object_get_data( tbl, "freeze" )) {
3501         return;
3502     }
3504     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3506     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3507                                                             "tolerance");
3508         
3509         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3510     double v = prefs->getDouble("tools.freehand.pencil", "tolerance", adj->value);
3511     gtk_adjustment_set_value(adj, v);
3512     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3516 static Inkscape::XML::NodeEventVector pencil_node_events =
3518     NULL,
3519     NULL,
3520     sp_pencil_tb_tolerance_value_changed_external,
3521     NULL,
3522     NULL,
3523 };
3526 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3528     sp_add_freehand_mode_toggle(mainActions, holder, true);
3530     EgeAdjustmentAction* eact = 0;
3532     /* Tolerance */
3533     {
3534         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3535         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3536         eact = create_adjustment_action( "PencilToleranceAction",
3537                                          _("Smoothing:"), _("Smoothing: "),
3538                  _("How much smoothing (simplifying) is applied to the line"),
3539                                          "tools.freehand.pencil", "tolerance",
3540                                          3.0,
3541                                          GTK_WIDGET(desktop->canvas), NULL,
3542                                          holder, TRUE, "altx-pencil",
3543                                          1, 100.0, 0.5, 0,
3544                                          labels, values, G_N_ELEMENTS(labels),
3545                                          sp_pencil_tb_tolerance_value_changed,
3546                                          1, 2);
3547         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3548         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3550         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3551                                                       "tools.freehand.pencil");
3552         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3553         g_object_set_data(G_OBJECT(holder), "repr", repr);
3555     }
3557     /* advanced shape options */
3558     freehand_add_advanced_shape_options(mainActions, holder, true);
3560     /* Reset */
3561     {
3562         InkAction* inky = ink_action_new( "PencilResetAction",
3563                                           _("Defaults"),
3564                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3565                                           GTK_STOCK_CLEAR,
3566                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3567         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3568         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3569     }
3571     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3576 //########################
3577 //##       Tweak        ##
3578 //########################
3580 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3582         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3583     prefs->setDouble( "tools.tweak", "width", adj->value * 0.01 );
3586 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3588         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3589     prefs->setDouble( "tools.tweak", "force", adj->value * 0.01 );
3592 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3594         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3595     prefs->setBool("tools.tweak", "usepressure", gtk_toggle_action_get_active(act));
3598 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3600     int mode = ege_select_one_action_get_active( act );
3601     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3602     prefs->setInt("tools.tweak", "mode", mode);
3604     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3605     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3606     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3607     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3608     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3609     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3610     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3611         if (doh) gtk_action_set_sensitive (doh, TRUE);
3612         if (dos) gtk_action_set_sensitive (dos, TRUE);
3613         if (dol) gtk_action_set_sensitive (dol, TRUE);
3614         if (doo) gtk_action_set_sensitive (doo, TRUE);
3615         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3616         if (fid) gtk_action_set_sensitive (fid, FALSE);
3617     } else {
3618         if (doh) gtk_action_set_sensitive (doh, FALSE);
3619         if (dos) gtk_action_set_sensitive (dos, FALSE);
3620         if (dol) gtk_action_set_sensitive (dol, FALSE);
3621         if (doo) gtk_action_set_sensitive (doo, FALSE);
3622         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3623         if (fid) gtk_action_set_sensitive (fid, TRUE);
3624     }
3627 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3629         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3630     prefs->setDouble( "tools.tweak", "fidelity", adj->value * 0.01 );
3633 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3634         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3635     prefs->setBool("tools.tweak", "doh", gtk_toggle_action_get_active(act));
3637 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3638         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3639     prefs->setBool("tools.tweak", "dos", gtk_toggle_action_get_active(act));
3641 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3642         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3643     prefs->setBool("tools.tweak", "dol", gtk_toggle_action_get_active(act));
3645 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3646         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3647     prefs->setBool("tools.tweak", "doo", gtk_toggle_action_get_active(act));
3650 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3652     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3653     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3655     {
3656         /* Width */
3657         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3658         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3659         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3660                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3661                                                               "tools.tweak", "width", 15,
3662                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3663                                                               1, 100, 1.0, 0.0,
3664                                                               labels, values, G_N_ELEMENTS(labels),
3665                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3666         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3667         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3668         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3669     }
3672     {
3673         /* Force */
3674         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3675         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3676         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3677                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3678                                                               "tools.tweak", "force", 20,
3679                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3680                                                               1, 100, 1.0, 0.0,
3681                                                               labels, values, G_N_ELEMENTS(labels),
3682                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3683         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3684         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3685         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3686     }
3688     /* Mode */
3689     {
3690         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3692         GtkTreeIter iter;
3693         gtk_list_store_append( model, &iter );
3694         gtk_list_store_set( model, &iter,
3695                             0, _("Push mode"),
3696                             1, _("Push parts of paths in any direction"),
3697                             2, "tweak_push_mode",
3698                             -1 );
3700         gtk_list_store_append( model, &iter );
3701         gtk_list_store_set( model, &iter,
3702                             0, _("Shrink mode"),
3703                             1, _("Shrink (inset) parts of paths"),
3704                             2, "tweak_shrink_mode",
3705                             -1 );
3707         gtk_list_store_append( model, &iter );
3708         gtk_list_store_set( model, &iter,
3709                             0, _("Grow mode"),
3710                             1, _("Grow (outset) parts of paths"),
3711                             2, "tweak_grow_mode",
3712                             -1 );
3714         gtk_list_store_append( model, &iter );
3715         gtk_list_store_set( model, &iter,
3716                             0, _("Attract mode"),
3717                             1, _("Attract parts of paths towards cursor"),
3718                             2, "tweak_attract_mode",
3719                             -1 );
3721         gtk_list_store_append( model, &iter );
3722         gtk_list_store_set( model, &iter,
3723                             0, _("Repel mode"),
3724                             1, _("Repel parts of paths from cursor"),
3725                             2, "tweak_repel_mode",
3726                             -1 );
3728         gtk_list_store_append( model, &iter );
3729         gtk_list_store_set( model, &iter,
3730                             0, _("Roughen mode"),
3731                             1, _("Roughen parts of paths"),
3732                             2, "tweak_roughen_mode",
3733                             -1 );
3735         gtk_list_store_append( model, &iter );
3736         gtk_list_store_set( model, &iter,
3737                             0, _("Color paint mode"),
3738                             1, _("Paint the tool's color upon selected objects"),
3739                             2, "tweak_colorpaint_mode",
3740                             -1 );
3742         gtk_list_store_append( model, &iter );
3743         gtk_list_store_set( model, &iter,
3744                             0, _("Color jitter mode"),
3745                             1, _("Jitter the colors of selected objects"),
3746                             2, "tweak_colorjitter_mode",
3747                             -1 );
3749         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3750         g_object_set( act, "short_label", _("Mode:"), NULL );
3751         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3752         g_object_set_data( holder, "mode_action", act );
3754         ege_select_one_action_set_appearance( act, "full" );
3755         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3756         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3757         ege_select_one_action_set_icon_column( act, 2 );
3758         ege_select_one_action_set_icon_size( act, secondarySize );
3759         ege_select_one_action_set_tooltip_column( act, 1  );
3761         gint mode = prefs->getInt("tools.tweak", "mode", 0);
3762         ege_select_one_action_set_active( act, mode );
3763         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3765         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3766     }
3768     guint mode = prefs->getInt("tools.tweak", "mode", 0);
3770     {
3771         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3772         ege_output_action_set_use_markup( act, TRUE );
3773         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3774         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3775             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3776         g_object_set_data( holder, "tweak_channels_label", act);
3777     }
3779     {
3780         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3781                                                       _("Hue"),
3782                                                       _("In color mode, act on objects' hue"),
3783                                                       NULL,
3784                                                       Inkscape::ICON_SIZE_DECORATION );
3785         //TRANSLATORS:  "H" here stands for hue
3786         g_object_set( act, "short_label", _("H"), NULL );
3787         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3788         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3789         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doh", 1 ) );
3790         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3791             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3792         g_object_set_data( holder, "tweak_doh", act);
3793     }
3794     {
3795         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3796                                                       _("Saturation"),
3797                                                       _("In color mode, act on objects' saturation"),
3798                                                       NULL,
3799                                                       Inkscape::ICON_SIZE_DECORATION );
3800         //TRANSLATORS: "S" here stands for Saturation
3801         g_object_set( act, "short_label", _("S"), NULL );
3802         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3803         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3804         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dos", 1 ) );
3805         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3806             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3807         g_object_set_data( holder, "tweak_dos", act );
3808     }
3809     {
3810         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3811                                                       _("Lightness"),
3812                                                       _("In color mode, act on objects' lightness"),
3813                                                       NULL,
3814                                                       Inkscape::ICON_SIZE_DECORATION );
3815         //TRANSLATORS: "L" here stands for Lightness
3816         g_object_set( act, "short_label", _("L"), NULL );
3817         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3818         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3819         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dol", 1 ) );
3820         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3821             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3822         g_object_set_data( holder, "tweak_dol", act );
3823     }
3824     {
3825         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3826                                                       _("Opacity"),
3827                                                       _("In color mode, act on objects' opacity"),
3828                                                       NULL,
3829                                                       Inkscape::ICON_SIZE_DECORATION );
3830         //TRANSLATORS: "O" here stands for Opacity
3831         g_object_set( act, "short_label", _("O"), NULL );
3832         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3833         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3834         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doo", 1 ) );
3835         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3836             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3837         g_object_set_data( holder, "tweak_doo", act );
3838     }
3840     {   /* Fidelity */
3841         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3842         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3843         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3844                                                               _("Fidelity"), _("Fidelity:"),
3845                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3846                                                               "tools.tweak", "fidelity", 50,
3847                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3848                                                               1, 100, 1.0, 10.0,
3849                                                               labels, values, G_N_ELEMENTS(labels),
3850                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3851         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3852         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3853         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3854             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3855         g_object_set_data( holder, "tweak_fidelity", eact );
3856     }
3859     /* Use Pressure button */
3860     {
3861         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3862                                                       _("Pressure"),
3863                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3864                                                       "use_pressure",
3865                                                       Inkscape::ICON_SIZE_DECORATION );
3866         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3867         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3868         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.tweak", "usepressure", true) );
3869     }
3874 //########################
3875 //##     Calligraphy    ##
3876 //########################
3877 static void update_presets_list (GObject *tbl)
3879         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3880     if (g_object_get_data(tbl, "presets_blocked"))
3881         return;
3883     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3884     if (!sel) {
3885         ege_select_one_action_set_active(sel, 0);
3886         return;
3887     }
3889     int total_prefs = prefs->childCount("tools.calligraphic.preset");
3891     for (int i = 0; i < total_prefs; ++i) {
3892         Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", i);
3893         /// @todo Remove the use of _getNode()
3894         Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
3896         bool match = true;
3898         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3899               iter;
3900               ++iter ) {
3901             const gchar *attr_name = g_quark_to_string(iter->key);
3902             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3903                 continue;
3904             void *widget = g_object_get_data(tbl, attr_name);
3905             if (widget) {
3906                 if (GTK_IS_ADJUSTMENT(widget)) {
3907                     double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3908                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3909                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3910                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3911                         match = false;
3912                         break;
3913                     }
3914                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3915                     bool v = prefs->getBool(preset_path, attr_name);
3916                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3917                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3918                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
3919                         match = false;
3920                         break;
3921                     }
3922                 }
3923             }
3924         }
3926         if (match) {
3927             // newly added item is at the same index as the
3928             // save command, so we need to change twice for it to take effect
3929             ege_select_one_action_set_active(sel, 0);
3930             ege_select_one_action_set_active(sel, i);
3931             return;
3932         }
3933     }
3935     // no match found
3936     ege_select_one_action_set_active(sel, 0);
3939 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3941         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3942     prefs->setDouble( "tools.calligraphic", "mass", adj->value * 0.01 );
3943     update_presets_list(tbl);
3946 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3948         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3949     prefs->setDouble( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3950     update_presets_list(tbl);
3953 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3955         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3956     prefs->setDouble( "tools.calligraphic", "angle", adj->value );
3957     update_presets_list(tbl);
3960 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3962         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3963     prefs->setDouble( "tools.calligraphic", "width", adj->value * 0.01 );
3964     update_presets_list(tbl);
3967 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3969         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3970     prefs->setDouble("tools.calligraphic", "thinning", adj->value * 0.01 );
3971     update_presets_list(tbl);
3974 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3976         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3977     prefs->setDouble( "tools.calligraphic", "flatness", adj->value * 0.01);
3978     update_presets_list(tbl);
3981 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3983         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3984     prefs->setDouble( "tools.calligraphic", "tremor", adj->value * 0.01 );
3985     update_presets_list(tbl);
3988 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3990         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3991     prefs->setDouble( "tools.calligraphic", "cap_rounding", adj->value );
3992     update_presets_list(tbl);
3995 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3997         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3998     prefs->setBool("tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ));
3999     update_presets_list(tbl);
4002 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4004         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4005     prefs->setBool("tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ));
4006     update_presets_list(tbl);
4009 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4011         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4012     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4013     prefs->setBool("tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ));
4014     update_presets_list(tbl);
4015     if (calligraphy_angle )
4016         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4020 static gchar const *const widget_names[] = {
4021     "width",
4022     "mass",
4023     "wiggle",
4024     "angle",
4025     "thinning",
4026     "tremor",
4027     "flatness",
4028     "cap_rounding",
4029     "usepressure",
4030     "tracebackground",
4031     "usetilt"
4032 };
4035 static void sp_dcc_build_presets_list(GObject *tbl)
4037     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4039     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4040     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4041     gtk_list_store_clear (model);
4043     {
4044         GtkTreeIter iter;
4045         gtk_list_store_append( model, &iter );
4046         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4047     }
4049     /// @todo Use public Preferences API instead of node manipulation
4050     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4051     Inkscape::XML::Node *repr = prefs->_getNode("tools.calligraphic.preset", true);
4052     Inkscape::XML::Node *child_repr = repr->firstChild();
4053     int ii=1;
4054     while (child_repr) {
4055         GtkTreeIter iter;
4056         char *preset_name = (char *) child_repr->attribute("name");
4057         gtk_list_store_append( model, &iter );
4058         gtk_list_store_set( model, &iter, 0, preset_name, 1, ii++, -1 );
4059         child_repr = child_repr->next();
4060     }
4062     {
4063         GtkTreeIter iter;
4064         gtk_list_store_append( model, &iter );
4065         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4066         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4067     }
4069     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4071     update_presets_list (tbl);
4074 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4076         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4077     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4078     if (! desktop) return;
4080     if (g_object_get_data(tbl, "presets_blocked"))
4081         return;
4083     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4084     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4085         // dialog cancelled
4086         update_presets_list (tbl);
4087         return;
4088     }
4089     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4091     if (profile_name.empty()) {
4092         // empty name entered
4093         update_presets_list (tbl);
4094         return;
4095     }
4097     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4099     int new_index = -1;
4100     Glib::ustring pref_path;
4101     int total_prefs = prefs->childCount("tools.calligraphic.preset");
4103     for (int i = 0; i < total_prefs; ++i) {
4104         Glib::ustring path = prefs->getNthChild("tools.calligraphic.preset", i);
4105         Glib::ustring name = prefs->getString(path, "name");
4106         if (!name.empty() && ( profile_name == name )) {
4107             // we already have preset with this name, replace its values
4108             new_index = i;
4109             pref_path = path;
4110             break;
4111         }
4112     }
4114     if (new_index == -1) {
4115         // no preset with this name, create
4116         /// @todo This is wrong, the name should be encoded in the key
4117         /// to allow deletion of presets
4118         new_index = total_prefs + 1;
4119         gchar *profile_id = g_strdup_printf(".dcc%d", new_index);
4120         pref_path = Glib::ustring("tools.calligraphic.preset") + profile_id;
4121         g_free(profile_id);
4122     }
4124     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4125         gchar const *const widget_name = widget_names[i];
4126         void *widget = g_object_get_data(tbl, widget_name);
4127         if (widget) {
4128             if (GTK_IS_ADJUSTMENT(widget)) {
4129                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4130                 double v = gtk_adjustment_get_value(adj);
4131                 prefs->setDouble(pref_path, widget_name, v);
4132                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4133             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4134                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4135                 bool v = gtk_toggle_action_get_active(toggle);
4136                 prefs->setBool(pref_path, widget_name, v);
4137                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4138             } else {
4139                g_warning("Unknown widget type for preset: %s\n", widget_name);
4140             }
4141         } else {
4142             g_warning("Bad key when writing preset: %s\n", widget_name);
4143         }
4144     }
4145     prefs->setString(pref_path, "name", profile_name);
4147     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4148     sp_dcc_build_presets_list (tbl);
4152 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4154         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4155         
4156     gint preset_index = ege_select_one_action_get_active( act );
4157     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4159     if (preset_index == save_presets_index) {
4160         // this is the Save command
4161         sp_dcc_save_profile(NULL, tbl);
4162         return;
4163     }
4165     if (g_object_get_data(tbl, "presets_blocked"))
4166         return;
4168         // preset_index is one-based so we subtract 1
4169     Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", preset_index - 1);
4171     if (!preset_path.empty()) {
4172         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
4174                 /// @todo Remove the use of _getNode() in this fragment, modify
4175                 /// the public interface of Preferences if necessary
4176         Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
4178                 // Shouldn't this be std::map?
4179         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4180               iter;
4181               ++iter ) {
4182             const gchar *attr_name = g_quark_to_string(iter->key);
4183             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4184                 continue;
4185             void *widget = g_object_get_data(tbl, attr_name);
4186             if (widget) {
4187                 if (GTK_IS_ADJUSTMENT(widget)) {
4188                     double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4189                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4190                     gtk_adjustment_set_value(adj, v);
4191                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4192                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4193                     int v = prefs->getInt(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4194                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4195                     gtk_toggle_action_set_active(toggle, v);
4196                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4197                 } else {
4198                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4199                 }
4200             } else {
4201                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4202             }
4203         }
4204         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4205     }
4210 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4212         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4213     {
4214         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4216         EgeAdjustmentAction* calligraphy_angle = 0;
4218         {
4219         /* Width */
4220         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4221         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4222         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4223                                                               _("Pen Width"), _("Width:"),
4224                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4225                                                               "tools.calligraphic", "width", 15,
4226                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4227                                                               1, 100, 1.0, 0.0,
4228                                                               labels, values, G_N_ELEMENTS(labels),
4229                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4230         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4231         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4232         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4233         }
4235         {
4236         /* Thinning */
4237             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4238             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4239         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4240                                                               _("Stroke Thinning"), _("Thinning:"),
4241                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4242                                                               "tools.calligraphic", "thinning", 10,
4243                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4244                                                               -100, 100, 1, 0.1,
4245                                                               labels, values, G_N_ELEMENTS(labels),
4246                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4247         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4248         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4249         }
4251         {
4252         /* Angle */
4253         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4254         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4255         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4256                                                               _("Pen Angle"), _("Angle:"),
4257                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4258                                                               "tools.calligraphic", "angle", 30,
4259                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4260                                                               -90.0, 90.0, 1.0, 10.0,
4261                                                               labels, values, G_N_ELEMENTS(labels),
4262                                                               sp_ddc_angle_value_changed, 1, 0 );
4263         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4264         g_object_set_data( holder, "angle_action", eact );
4265         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4266         calligraphy_angle = eact;
4267         }
4269         {
4270         /* Fixation */
4271             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4272         gdouble values[] = {0, 20, 40, 60, 90, 100};
4273         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4274                                                               _("Fixation"), _("Fixation:"),
4275                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4276                                                               "tools.calligraphic", "flatness", 90,
4277                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4278                                                               0.0, 100, 1.0, 10.0,
4279                                                               labels, values, G_N_ELEMENTS(labels),
4280                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4281         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4282         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4283         }
4285         {
4286         /* Cap Rounding */
4287             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4288         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4289         // TRANSLATORS: "cap" means "end" (both start and finish) here
4290         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4291                                                               _("Cap rounding"), _("Caps:"),
4292                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4293                                                               "tools.calligraphic", "cap_rounding", 0.0,
4294                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4295                                                               0.0, 5.0, 0.01, 0.1,
4296                                                               labels, values, G_N_ELEMENTS(labels),
4297                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4298         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4299         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4300         }
4302         {
4303         /* Tremor */
4304             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4305         gdouble values[] = {0, 10, 20, 40, 60, 100};
4306         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4307                                                               _("Stroke Tremor"), _("Tremor:"),
4308                                                               _("Increase to make strokes rugged and trembling"),
4309                                                               "tools.calligraphic", "tremor", 0.0,
4310                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4311                                                               0.0, 100, 1, 0.0,
4312                                                               labels, values, G_N_ELEMENTS(labels),
4313                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4315         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4316         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4317         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4318         }
4320         {
4321         /* Wiggle */
4322         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4323         gdouble values[] = {0, 20, 40, 60, 100};
4324         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4325                                                               _("Pen Wiggle"), _("Wiggle:"),
4326                                                               _("Increase to make the pen waver and wiggle"),
4327                                                               "tools.calligraphic", "wiggle", 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_wiggle_value_changed, 0.01, 0, 100 );
4332         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4333         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4334         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4335         }
4337         {
4338         /* Mass */
4339             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4340         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4341         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4342                                                               _("Pen Mass"), _("Mass:"),
4343                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4344                                                               "tools.calligraphic", "mass", 2.0,
4345                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4346                                                               0.0, 100, 1, 0.0,
4347                                                               labels, values, G_N_ELEMENTS(labels),
4348                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4349         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4350         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4351         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4352         }
4355         /* Trace Background button */
4356         {
4357             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4358                                                           _("Trace Background"),
4359                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4360                                                           "trace_background",
4361                                                           Inkscape::ICON_SIZE_DECORATION );
4362             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4363             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4364             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "tracebackground", false) );
4365             g_object_set_data( holder, "tracebackground", act );
4366         }
4368         /* Use Pressure button */
4369         {
4370             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4371                                                           _("Pressure"),
4372                                                           _("Use the pressure of the input device to alter the width of the pen"),
4373                                                           "use_pressure",
4374                                                           Inkscape::ICON_SIZE_DECORATION );
4375             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4376             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4377             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usepressure", true) );
4378             g_object_set_data( holder, "usepressure", act );
4379         }
4381         /* Use Tilt button */
4382         {
4383             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4384                                                           _("Tilt"),
4385                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4386                                                           "use_tilt",
4387                                                           Inkscape::ICON_SIZE_DECORATION );
4388             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4389             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4390             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("tools.calligraphic", "usetilt", true) );
4391             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usetilt", true) );
4392             g_object_set_data( holder, "usetilt", act );
4393         }
4395         /*calligraphic profile */
4396         {
4397             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4398             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4399             ege_select_one_action_set_appearance (act1, "compact");
4400             g_object_set_data (holder, "profile_selector", act1 );
4402             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4404             sp_dcc_build_presets_list (holder);
4406             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4407             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4408         }
4409     }
4413 //########################
4414 //##    Circle / Arc    ##
4415 //########################
4417 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4419     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4420     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4422     if (v1 == 0 && v2 == 0) {
4423         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4424             gtk_action_set_sensitive( ocb, FALSE );
4425             gtk_action_set_sensitive( make_whole, FALSE );
4426         }
4427     } else {
4428         gtk_action_set_sensitive( ocb, TRUE );
4429         gtk_action_set_sensitive( make_whole, TRUE );
4430     }
4433 static void
4434 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4436     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4438     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4439         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4440         prefs->setDouble("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4441     }
4443     // quit if run by the attr_changed listener
4444     if (g_object_get_data( tbl, "freeze" )) {
4445         return;
4446     }
4448     // in turn, prevent listener from responding
4449     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4451     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4453     bool modmade = false;
4454     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4455          items != NULL;
4456          items = items->next)
4457     {
4458         SPItem *item = SP_ITEM(items->data);
4460         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4462             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4463             SPArc *arc = SP_ARC(item);
4465             if (!strcmp(value_name, "start"))
4466                 ge->start = (adj->value * M_PI)/ 180;
4467             else
4468                 ge->end = (adj->value * M_PI)/ 180;
4470             sp_genericellipse_normalize(ge);
4471             ((SPObject *)arc)->updateRepr();
4472             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4474             modmade = true;
4475         }
4476     }
4478     g_free(namespaced_name);
4480     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4482     sp_arctb_sensitivize( tbl, adj->value, other->value );
4484     if (modmade) {
4485         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4486                                    _("Arc: Change start/end"));
4487     }
4489     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4493 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4495     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4498 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4500     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4504 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4506     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4507     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4508         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4509         prefs->setBool("tools.shapes.arc", "open", ege_select_one_action_get_active(act) != 0);
4510     }
4512     // quit if run by the attr_changed listener
4513     if (g_object_get_data( tbl, "freeze" )) {
4514         return;
4515     }
4517     // in turn, prevent listener from responding
4518     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4520     bool modmade = false;
4522     if ( ege_select_one_action_get_active(act) != 0 ) {
4523         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4524              items != NULL;
4525              items = items->next)
4526         {
4527             if (SP_IS_ARC((SPItem *) items->data)) {
4528                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4529                 repr->setAttribute("sodipodi:open", "true");
4530                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4531                 modmade = true;
4532             }
4533         }
4534     } else {
4535         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4536              items != NULL;
4537              items = items->next)
4538         {
4539             if (SP_IS_ARC((SPItem *) items->data))    {
4540                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4541                 repr->setAttribute("sodipodi:open", NULL);
4542                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4543                 modmade = true;
4544             }
4545         }
4546     }
4548     if (modmade) {
4549         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4550                                    _("Arc: Change open/closed"));
4551     }
4553     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4556 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4558     GtkAdjustment *adj;
4559     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4560     gtk_adjustment_set_value(adj, 0.0);
4561     gtk_adjustment_value_changed(adj);
4563     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4564     gtk_adjustment_set_value(adj, 0.0);
4565     gtk_adjustment_value_changed(adj);
4567     spinbutton_defocus( GTK_OBJECT(obj) );
4570 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4571                                       gchar const */*old_value*/, gchar const */*new_value*/,
4572                                       bool /*is_interactive*/, gpointer data)
4574     GObject *tbl = G_OBJECT(data);
4576     // quit if run by the _changed callbacks
4577     if (g_object_get_data( tbl, "freeze" )) {
4578         return;
4579     }
4581     // in turn, prevent callbacks from responding
4582     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4584     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4585     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4587     GtkAdjustment *adj1,*adj2;
4588     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4589     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4590     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4591     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4593     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4595     char const *openstr = NULL;
4596     openstr = repr->attribute("sodipodi:open");
4597     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4599     if (openstr) {
4600         ege_select_one_action_set_active( ocb, 1 );
4601     } else {
4602         ege_select_one_action_set_active( ocb, 0 );
4603     }
4605     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4608 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4609     NULL, /* child_added */
4610     NULL, /* child_removed */
4611     arc_tb_event_attr_changed,
4612     NULL, /* content_changed */
4613     NULL  /* order_changed */
4614 };
4617 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4619     int n_selected = 0;
4620     Inkscape::XML::Node *repr = NULL;
4622     purge_repr_listener( tbl, tbl );
4624     for (GSList const *items = selection->itemList();
4625          items != NULL;
4626          items = items->next)
4627     {
4628         if (SP_IS_ARC((SPItem *) items->data)) {
4629             n_selected++;
4630             repr = SP_OBJECT_REPR((SPItem *) items->data);
4631         }
4632     }
4634     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4636     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4637     if (n_selected == 0) {
4638         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4639     } else if (n_selected == 1) {
4640         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4641         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4643         if (repr) {
4644             g_object_set_data( tbl, "repr", repr );
4645             Inkscape::GC::anchor(repr);
4646             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4647             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4648         }
4649     } else {
4650         // FIXME: implement averaging of all parameters for multiple selected
4651         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4652         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4653         sp_arctb_sensitivize( tbl, 1, 0 );
4654     }
4658 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4660         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4661         
4662     EgeAdjustmentAction* eact = 0;
4663     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4666     {
4667         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4668         ege_output_action_set_use_markup( act, TRUE );
4669         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4670         g_object_set_data( holder, "mode_action", act );
4671     }
4673     /* Start */
4674     {
4675         eact = create_adjustment_action( "ArcStartAction",
4676                                          _("Start"), _("Start:"),
4677                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4678                                          "tools.shapes.arc", "start", 0.0,
4679                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4680                                          -360.0, 360.0, 1.0, 10.0,
4681                                          0, 0, 0,
4682                                          sp_arctb_start_value_changed);
4683         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4684     }
4686     /* End */
4687     {
4688         eact = create_adjustment_action( "ArcEndAction",
4689                                          _("End"), _("End:"),
4690                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4691                                          "tools.shapes.arc", "end", 0.0,
4692                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4693                                          -360.0, 360.0, 1.0, 10.0,
4694                                          0, 0, 0,
4695                                          sp_arctb_end_value_changed);
4696         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4697     }
4699     /* Segments / Pie checkbox */
4700     {
4701         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4703         GtkTreeIter iter;
4704         gtk_list_store_append( model, &iter );
4705         gtk_list_store_set( model, &iter,
4706                             0, _("Closed arc"),
4707                             1, _("Switch to segment (closed shape with two radii)"),
4708                             2, "circle_closed_arc",
4709                             -1 );
4711         gtk_list_store_append( model, &iter );
4712         gtk_list_store_set( model, &iter,
4713                             0, _("Open Arc"),
4714                             1, _("Switch to arc (unclosed shape)"),
4715                             2, "circle_open_arc",
4716                             -1 );
4718         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4719         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4720         g_object_set_data( holder, "open_action", act );
4722         ege_select_one_action_set_appearance( act, "full" );
4723         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4724         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4725         ege_select_one_action_set_icon_column( act, 2 );
4726         ege_select_one_action_set_icon_size( act, secondarySize );
4727         ege_select_one_action_set_tooltip_column( act, 1  );
4729         bool isClosed = !prefs->getBool("tools.shapes.arc", "open", false);
4730         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4731         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4732     }
4734     /* Make Whole */
4735     {
4736         InkAction* inky = ink_action_new( "ArcResetAction",
4737                                           _("Make whole"),
4738                                           _("Make the shape a whole ellipse, not arc or segment"),
4739                                           "reset_circle",
4740                                           secondarySize );
4741         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4742         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4743         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4744         g_object_set_data( holder, "make_whole", inky );
4745     }
4747     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4748     // sensitivize make whole and open checkbox
4749     {
4750         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4751         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4752         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4753     }
4756     sigc::connection *connection = new sigc::connection(
4757         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4758         );
4759     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4760     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4766 // toggle button callbacks and updaters
4768 //########################
4769 //##      Dropper       ##
4770 //########################
4772 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4773         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4774     prefs->setInt( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4775     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4776     if ( set_action ) {
4777         if ( gtk_toggle_action_get_active( act ) ) {
4778             gtk_action_set_sensitive( set_action, TRUE );
4779         } else {
4780             gtk_action_set_sensitive( set_action, FALSE );
4781         }
4782     }
4784     spinbutton_defocus(GTK_OBJECT(tbl));
4787 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4788         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4789     prefs->setInt( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4790     spinbutton_defocus(GTK_OBJECT(tbl));
4794 /**
4795  * Dropper auxiliary toolbar construction and setup.
4796  *
4797  * TODO: Would like to add swatch of current color.
4798  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4799  *       can drag and drop places. Will provide a nice mixing palette.
4800  */
4801 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4803         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4804     gint pickAlpha = prefs->getInt( "tools.dropper", "pick", 1 );
4806     {
4807         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4808         ege_output_action_set_use_markup( act, TRUE );
4809         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4810     }
4812     {
4813         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4814                                                       _("Pick opacity"),
4815                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4816                                                       NULL,
4817                                                       Inkscape::ICON_SIZE_DECORATION );
4818         g_object_set( act, "short_label", _("Pick"), NULL );
4819         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4820         g_object_set_data( holder, "pick_action", act );
4821         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4822         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4823     }
4825     {
4826         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4827                                                       _("Assign opacity"),
4828                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4829                                                       NULL,
4830                                                       Inkscape::ICON_SIZE_DECORATION );
4831         g_object_set( act, "short_label", _("Assign"), NULL );
4832         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4833         g_object_set_data( holder, "set_action", act );
4834         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.dropper", "setalpha", 1 ) );
4835         // make sure it's disabled if we're not picking alpha
4836         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4837         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4838     }
4842 //########################
4843 //##      LPETool       ##
4844 //########################
4846 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4848 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4849 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4851     using namespace Inkscape::LivePathEffect;
4853     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4854     SPEventContext *ec = desktop->event_context;
4855     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4856         return;
4857     }
4859     // only take action if run by the attr_changed listener
4860     if (!g_object_get_data(tbl, "freeze")) {
4861         // in turn, prevent listener from responding
4862         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4864         gint mode = ege_select_one_action_get_active(act);
4865         EffectType type = lpesubtools[mode];
4867         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4868         bool success = lpetool_try_construction(lc, type);
4869         if (success) {
4870             // since the construction was already performed, we set the state back to inactive
4871             ege_select_one_action_set_active(act, 0);
4872             mode = 0;
4873         } else {
4874             // switch to the chosen subtool
4875             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4876         }
4878         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4879                 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4880             prefs->setInt( "tools.lpetool", "mode", mode );
4881         }
4883         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4884     }
4887 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
4889     SPEventContext *ec = selection->desktop()->event_context;
4890     if (!SP_IS_LPETOOL_CONTEXT(ec))
4891         return;
4893     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4896 void
4897 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4899     using namespace Inkscape::LivePathEffect;
4900     SPEventContext *ec = selection->desktop()->event_context;
4901     if (!SP_IS_LPETOOL_CONTEXT(ec))
4902         return;
4903     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4905     lpetool_delete_measuring_items(lc);
4906     lpetool_create_measuring_items(lc, selection);
4908     // activate line segment combo box if a single item with LPELineSegment is selected
4909     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4910     SPItem *item = selection->singleItem();
4911     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4912         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4913         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4914         if (lpe && lpe->effectType() == LINE_SEGMENT) {
4915             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4916             g_object_set_data(tbl, "currentlpe", lpe);
4917             g_object_set_data(tbl, "currentlpeitem", lpeitem);
4918             gtk_action_set_sensitive(w, TRUE);
4919             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4920         } else {
4921             g_object_set_data(tbl, "currentlpe", NULL);
4922             g_object_set_data(tbl, "currentlpeitem", NULL);
4923             gtk_action_set_sensitive(w, FALSE);
4924         }
4925     } else {
4926         g_object_set_data(tbl, "currentlpe", NULL);
4927         g_object_set_data(tbl, "currentlpeitem", NULL);
4928         gtk_action_set_sensitive(w, FALSE);
4929     }
4932 static void
4933 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4934     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4935     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4937     bool show = gtk_toggle_action_get_active( act );
4938     prefs->setInt("tools.lpetool", "show_bbox",  show ? 1 : 0);
4940     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4941         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4942         lpetool_context_reset_limiting_bbox(lc);
4943     }
4946 static void
4947 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4948     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4949     if (!tools_isactive(desktop, TOOLS_LPETOOL))
4950         return;
4952     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4953     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4954     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4955         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4956         bool show = gtk_toggle_action_get_active( act );
4957         prefs->setInt("tools.lpetool", "show_measuring_info",  show ? 1 : 0);
4958         lpetool_show_measuring_info(lc, show);
4959         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4960     }
4963 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
4964     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4965     SPUnit const *unit = tracker->getActiveUnit();
4966     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4967     prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
4969     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4970     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4971         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4972         lpetool_delete_measuring_items(lc);
4973         lpetool_create_measuring_items(lc);
4974     }
4977 static void
4978 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4979     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4980     Inkscape::Selection *selection = desktop->selection;
4982     boost::optional<NR::Rect> bbox = selection->bounds();
4984     if (bbox) {
4985         Geom::Point A(bbox->min());
4986         Geom::Point B(bbox->max());
4988         A *= desktop->doc2dt();
4989         B *= desktop->doc2dt();
4991         // TODO: should we provide a way to store points in prefs?
4992         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4993         prefs->setDouble("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4994         prefs->setDouble("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4995         prefs->setDouble("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4996         prefs->setDouble("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4998         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4999     }
5001     gtk_toggle_action_set_active(act, false);
5004 static void
5005 sp_line_segment_build_list(GObject *tbl)
5007     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5009     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5010     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5011     gtk_list_store_clear (model);
5013     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5014     {
5015         GtkTreeIter iter;
5016         gtk_list_store_append( model, &iter );
5017         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5018         gtk_list_store_append( model, &iter );
5019         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5020         gtk_list_store_append( model, &iter );
5021         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5022         gtk_list_store_append( model, &iter );
5023         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5024     }
5026     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5029 static void
5030 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5031     using namespace Inkscape::LivePathEffect;
5033     // quit if run by the attr_changed listener
5034     if (g_object_get_data(tbl, "freeze")) {
5035         return;
5036     }
5038     // in turn, prevent listener from responding
5039     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5041     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5042     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5043     if (lpeitem) {
5044         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5045         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5046         sp_lpe_item_update_patheffect(lpeitem, true, true);
5047     }
5049     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5052 static void
5053 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5054     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5056     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5057         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5058     }
5059     gtk_toggle_action_set_active(act, false);
5062 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5064     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5065     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5066     g_object_set_data(holder, "tracker", tracker);
5067     SPUnit const *unit = tracker->getActiveUnit();
5068     
5069     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5070     prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
5072     /** Automatically create a list of LPEs that get added to the toolbar **/
5073     {
5074         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5076         GtkTreeIter iter;
5078         // the first toggle button represents the state that no subtool is active (remove this when
5079         // this can be modeled by EgeSelectOneAction or some other action)
5080         gtk_list_store_append( model, &iter );
5081         gtk_list_store_set( model, &iter,
5082                             0, _("All inactive"),
5083                             1, _("No geometric tool is active"),
5084                             2, _("all_inactive"),
5085                             -1 );
5087         Inkscape::LivePathEffect::EffectType type;
5088         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5089             type =  lpesubtools[i];
5090             gtk_list_store_append( model, &iter );
5091             gtk_list_store_set( model, &iter,
5092                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5093                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5094                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5095                                 -1 );
5096         }
5098         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5099         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5100         g_object_set_data( holder, "lpetool_mode_action", act );
5102         ege_select_one_action_set_appearance( act, "full" );
5103         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5104         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5105         ege_select_one_action_set_icon_column( act, 2 );
5106         ege_select_one_action_set_tooltip_column( act, 1  );
5108         gint lpeToolMode = prefs->getInt("tools.lpetool", "mode", 0);
5109         ege_select_one_action_set_active( act, lpeToolMode );
5110         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5111     }
5113     /* Show limiting bounding box */
5114     {
5115         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5116                                                       _("Show limiting bounding box"),
5117                                                       _("Show bounding box (used to cut infinite lines)"),
5118                                                       "lpetool_show_bbox",
5119                                                       Inkscape::ICON_SIZE_DECORATION );
5120         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5121         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5122         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_bbox", 1 ) );
5123     }
5125     /* Set limiting bounding box to bbox of current selection */
5126     {
5127         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5128                                                       _("Get limiting bounding box from selection"),
5129                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5130                                                       "lpetool_set_bbox",
5131                                                       Inkscape::ICON_SIZE_DECORATION );
5132         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5133         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5134         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5135     }
5138     /* Combo box to choose line segment type */
5139     {
5140         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5141         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5142         ege_select_one_action_set_appearance (act, "compact");
5143         g_object_set_data (holder, "lpetool_line_segment_action", act );
5145         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5147         sp_line_segment_build_list (holder);
5149         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5150         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5151         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5152     }
5154     /* Display measuring info for selected items */
5155     {
5156         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5157                                                       _("Display measuring info"),
5158                                                       _("Display measuring info for selected items"),
5159                                                       "lpetool_measuring_info",
5160                                                       Inkscape::ICON_SIZE_DECORATION );
5161         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5162         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5163         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_measuring_info", 1 ) );
5164     }
5166     // add the units menu
5167     {
5168         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5169         gtk_action_group_add_action( mainActions, act );
5170         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5171         g_object_set_data(holder, "lpetool_units_action", act);
5172         gtk_action_set_sensitive(act, prefs->getInt("tools.lpetool", "show_measuring_info", 1));
5173     }
5175     /* Open LPE dialog (to adapt parameters numerically) */
5176     {
5177         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5178                                                       _("Open LPE dialog"),
5179                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5180                                                       "lpetool_open_lpe_dialog",
5181                                                       Inkscape::ICON_SIZE_DECORATION );
5182         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5183         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5184         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5185     }
5187     //watch selection
5188     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5190     sigc::connection *c_selection_modified =
5191         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5192                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5193     pool->add_connection ("selection-modified", c_selection_modified);
5195     sigc::connection *c_selection_changed =
5196         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5197                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5198     pool->add_connection ("selection-changed", c_selection_changed);
5201 //########################
5202 //##       Eraser       ##
5203 //########################
5205 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5207     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5208     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5209     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5210         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5211         prefs->setInt( "tools.eraser", "mode", eraserMode );
5212     }
5214     // only take action if run by the attr_changed listener
5215     if (!g_object_get_data( tbl, "freeze" )) {
5216         // in turn, prevent listener from responding
5217         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5219         if ( eraserMode != 0 ) {
5220         } else {
5221         }
5222         // TODO finish implementation
5224         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5225     }
5228 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5230     {
5231         /* Width */
5232         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5233         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5234         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5235                                                               _("Pen Width"), _("Width:"),
5236                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5237                                                               "tools.eraser", "width", 15,
5238                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5239                                                               1, 100, 1.0, 0.0,
5240                                                               labels, values, G_N_ELEMENTS(labels),
5241                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
5242         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5243         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5244         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5245     }
5247     {
5248         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5250         GtkTreeIter iter;
5251         gtk_list_store_append( model, &iter );
5252         gtk_list_store_set( model, &iter,
5253                             0, _("Delete"),
5254                             1, _("Delete objects touched by the eraser"),
5255                             2, "delete_object",
5256                             -1 );
5258         gtk_list_store_append( model, &iter );
5259         gtk_list_store_set( model, &iter,
5260                             0, _("Cut"),
5261                             1, _("Cut out from objects"),
5262                             2, "difference",
5263                             -1 );
5265         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5266         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5267         g_object_set_data( holder, "eraser_mode_action", act );
5269         ege_select_one_action_set_appearance( act, "full" );
5270         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5271         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5272         ege_select_one_action_set_icon_column( act, 2 );
5273         ege_select_one_action_set_tooltip_column( act, 1  );
5275                 /// @todo Convert to boolean?
5276                 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5277         gint eraserMode = (prefs->getInt("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5278         ege_select_one_action_set_active( act, eraserMode );
5279         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5280     }
5284 //########################
5285 //##    Text Toolbox    ##
5286 //########################
5287 /*
5288 static void
5289 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5291     //Call back for letter sizing spinbutton
5294 static void
5295 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5297     //Call back for line height spinbutton
5300 static void
5301 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5303     //Call back for horizontal kerning spinbutton
5306 static void
5307 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5309     //Call back for vertical kerning spinbutton
5312 static void
5313 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5315     //Call back for letter rotation spinbutton
5316 }*/
5318 namespace {
5320 bool popdown_visible = false;
5321 bool popdown_hasfocus = false;
5323 void
5324 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5326     SPStyle *query =
5327         sp_style_new (SP_ACTIVE_DOCUMENT);
5329 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5331     int result_family =
5332         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5334     int result_style =
5335         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5337     int result_numbers =
5338         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5340     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5342     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5343     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5344         // there are no texts in selection, read from prefs
5346         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5347         if (repr) {
5348             sp_style_read_from_repr (query, repr);
5349             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5350                 // do not reset the toolbar style from prefs if we already did it last time
5351                 sp_style_unref(query);
5352                 return;
5353             }
5354             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5355         } else {
5356             sp_style_unref(query);
5357             return;
5358         }
5359     } else {
5360         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5361     }
5363     if (query->text)
5364     {
5365         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5366             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5367             gtk_entry_set_text (GTK_ENTRY (entry), "");
5369         } else if (query->text->font_specification.value || query->text->font_family.value) {
5371             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5373             // Get the font that corresponds
5374             Glib::ustring familyName;
5376             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5377             if (font) {
5378                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5379                 font->Unref();
5380                 font = NULL;
5381             }
5383             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5385             Gtk::TreePath path;
5386             try {
5387                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5388             } catch (...) {
5389                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5390                 sp_style_unref(query);
5391                 return;
5392             }
5394             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5395             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5397             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5399             gtk_tree_selection_select_path (tselection, path.gobj());
5400             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5402             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5403         }
5405         //Size
5406         {
5407             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5408             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5409             g_object_set_data(tbl, "size-block", gpointer(1));
5410             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5411             g_object_set_data(tbl, "size-block", gpointer(0));
5412             g_free(str);
5413         }
5415         //Anchor
5416         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5417         {
5418             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5419             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5420             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5421             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5422         }
5423         else
5424         {
5425             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5426             {
5427                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5428                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5429                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5430                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5431             }
5432             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5433             {
5434                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5435                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5436                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5437                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5438             }
5439             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5440             {
5441                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5442                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5443                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5444                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5445             }
5446         }
5448         //Style
5449         {
5450             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5452             gboolean active = gtk_toggle_button_get_active (button);
5453             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5455             if (active != check)
5456             {
5457                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5458                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5459                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5460             }
5461         }
5463         {
5464             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5466             gboolean active = gtk_toggle_button_get_active (button);
5467             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5469             if (active != check)
5470             {
5471                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5472                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5473                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5474             }
5475         }
5477         //Orientation
5478         //locking both buttons, changing one affect all group (both)
5479         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5480         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5482         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5483         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5485         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5486         {
5487             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5488         }
5489         else
5490         {
5491             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5492         }
5493         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5494         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5495     }
5497     sp_style_unref(query);
5500 void
5501 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5503     sp_text_toolbox_selection_changed (selection, tbl);
5506 void
5507 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5509     sp_text_toolbox_selection_changed (NULL, tbl);
5512 void
5513 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5514                                 GObject             *tbl)
5516     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5517     GtkTreeModel *model = 0;
5518     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5519     GtkTreeIter   iter;
5520     char         *family = 0;
5522     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5523     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5525     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5526         return;
5527     }
5529     gtk_tree_model_get (model, &iter, 0, &family, -1);
5531     if (g_object_get_data (G_OBJECT (selection), "block"))
5532     {
5533         gtk_entry_set_text (GTK_ENTRY (entry), family);
5534         return;
5535     }
5537     gtk_entry_set_text (GTK_ENTRY (entry), family);
5539     SPStyle *query =
5540         sp_style_new (SP_ACTIVE_DOCUMENT);
5542     int result_fontspec =
5543         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5545     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5547     SPCSSAttr *css = sp_repr_css_attr_new ();
5550     // First try to get the font spec from the stored value
5551     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5553     if (fontSpec.empty()) {
5554         // Construct a new font specification if it does not yet exist
5555         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5556         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5557         fontFromStyle->Unref();
5558     }
5560     if (!fontSpec.empty()) {
5561         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5562         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5563             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5564             if (font) {
5565                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5567                 // Set all the these just in case they were altered when finding the best
5568                 // match for the new family and old style...
5570                 gchar c[256];
5572                 font->Family(c, 256);
5573                 sp_repr_css_set_property (css, "font-family", c);
5575                 font->Attribute( "weight", c, 256);
5576                 sp_repr_css_set_property (css, "font-weight", c);
5578                 font->Attribute("style", c, 256);
5579                 sp_repr_css_set_property (css, "font-style", c);
5581                 font->Attribute("stretch", c, 256);
5582                 sp_repr_css_set_property (css, "font-stretch", c);
5584                 font->Attribute("variant", c, 256);
5585                 sp_repr_css_set_property (css, "font-variant", c);
5587                 font->Unref();
5588             }
5589         }
5590     }
5592     // If querying returned nothing, set the default style of the tool (for new texts)
5593     if (result_fontspec == QUERY_STYLE_NOTHING)
5594     {
5595         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5596         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5597     }
5598     else
5599     {
5600         sp_desktop_set_style (desktop, css, true, true);
5601     }
5603     sp_style_unref(query);
5605     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5606                                    _("Text: Change font family"));
5607     sp_repr_css_attr_unref (css);
5608     g_free(family);
5609     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5611     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5614 /* This is where execution comes when the contents of the font family box have been completed
5615    by the press of the return key */
5616 void
5617 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5618                                        GObject      *tbl)
5620     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5622 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5623     try {
5624         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5625         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5626         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5627         gtk_tree_selection_select_path (selection, path.gobj());
5628         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5629         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5630     } catch (...) {
5631         if (family && strlen (family))
5632         {
5633             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5634         }
5635     }
5638 void
5639 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5640                                    gpointer          data)
5642     if (g_object_get_data (G_OBJECT (button), "block")) return;
5643     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5644     int prop = GPOINTER_TO_INT(data);
5646     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5647     SPCSSAttr *css = sp_repr_css_attr_new ();
5649     switch (prop)
5650     {
5651         case 0:
5652         {
5653             sp_repr_css_set_property (css, "text-anchor", "start");
5654             sp_repr_css_set_property (css, "text-align", "start");
5655             break;
5656         }
5657         case 1:
5658         {
5659             sp_repr_css_set_property (css, "text-anchor", "middle");
5660             sp_repr_css_set_property (css, "text-align", "center");
5661             break;
5662         }
5664         case 2:
5665         {
5666             sp_repr_css_set_property (css, "text-anchor", "end");
5667             sp_repr_css_set_property (css, "text-align", "end");
5668             break;
5669         }
5671         case 3:
5672         {
5673             sp_repr_css_set_property (css, "text-anchor", "start");
5674             sp_repr_css_set_property (css, "text-align", "justify");
5675             break;
5676         }
5677     }
5679     SPStyle *query =
5680         sp_style_new (SP_ACTIVE_DOCUMENT);
5681     int result_numbers =
5682         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5684     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5685     if (result_numbers == QUERY_STYLE_NOTHING)
5686     {
5687         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5688     }
5690     sp_style_unref(query);
5692     sp_desktop_set_style (desktop, css, true, true);
5693     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5694                                    _("Text: Change alignment"));
5695     sp_repr_css_attr_unref (css);
5697     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5700 void
5701 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5702                                gpointer          data)
5704     if (g_object_get_data (G_OBJECT (button), "block")) return;
5706     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5707     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5708     int          prop       = GPOINTER_TO_INT(data);
5709     bool         active     = gtk_toggle_button_get_active (button);
5711     SPStyle *query =
5712         sp_style_new (SP_ACTIVE_DOCUMENT);
5714     int result_fontspec =
5715         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5717     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5718     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5719     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5721     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5722     Glib::ustring newFontSpec = "";
5724     if (fontSpec.empty()) {
5725         // Construct a new font specification if it does not yet exist
5726         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5727         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5728         fontFromStyle->Unref();
5729     }
5731     switch (prop)
5732     {
5733         case 0:
5734         {
5735             if (!fontSpec.empty()) {
5736                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5737             }
5738             if (fontSpec != newFontSpec) {
5739                 // Don't even set the bold if the font didn't exist on the system
5740                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5741             }
5742             break;
5743         }
5745         case 1:
5746         {
5747             if (!fontSpec.empty()) {
5748                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5749             }
5750             if (fontSpec != newFontSpec) {
5751                 // Don't even set the italic if the font didn't exist on the system
5752                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5753             }
5754             break;
5755         }
5756     }
5758     if (!newFontSpec.empty()) {
5759         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5760     }
5762     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5763     if (result_fontspec == QUERY_STYLE_NOTHING)
5764     {
5765         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5766     }
5768     sp_style_unref(query);
5770     sp_desktop_set_style (desktop, css, true, true);
5771     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5772                                    _("Text: Change font style"));
5773     sp_repr_css_attr_unref (css);
5775     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5778 void
5779 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5780                                      gpointer         data)
5782     if (g_object_get_data (G_OBJECT (button), "block")) {
5783         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5784         return;
5785     }
5787     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5788     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5789     int          prop       = GPOINTER_TO_INT(data);
5791     switch (prop)
5792     {
5793         case 0:
5794         {
5795             sp_repr_css_set_property (css, "writing-mode", "lr");
5796             break;
5797         }
5799         case 1:
5800         {
5801             sp_repr_css_set_property (css, "writing-mode", "tb");
5802             break;
5803         }
5804     }
5806     SPStyle *query =
5807         sp_style_new (SP_ACTIVE_DOCUMENT);
5808     int result_numbers =
5809         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5811     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5812     if (result_numbers == QUERY_STYLE_NOTHING)
5813     {
5814         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5815     }
5817     sp_desktop_set_style (desktop, css, true, true);
5818     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5819                                    _("Text: Change orientation"));
5820     sp_repr_css_attr_unref (css);
5822     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5825 gboolean
5826 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5828     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5829     if (!desktop) return FALSE;
5831     switch (get_group0_keyval (event)) {
5832         case GDK_Escape: // defocus
5833             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5834             sp_text_toolbox_selection_changed (NULL, tbl); // update
5835             return TRUE; // I consumed the event
5836             break;
5837     }
5838     return FALSE;
5841 gboolean
5842 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5844     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5845     if (!desktop) return FALSE;
5847     switch (get_group0_keyval (event)) {
5848         case GDK_KP_Enter:
5849         case GDK_Return:
5850         case GDK_Escape: // defocus
5851             gtk_widget_hide (w);
5852             popdown_visible = false;
5853             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5854             return TRUE; // I consumed the event
5855             break;
5856         case GDK_w:
5857         case GDK_W:
5858             if (event->state & GDK_CONTROL_MASK) {
5859                 gtk_widget_hide (w);
5860                 popdown_visible = false;
5861                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5862                 return TRUE; // I consumed the event
5863             }
5864             break;
5865     }
5866     return FALSE;
5870 void
5871 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5872                                GObject     *tbl)
5874     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5876     if (g_object_get_data (tbl, "size-block")) return;
5878     // If this is not from selecting a size in the list (in which case get_active will give the
5879     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5880     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5881     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5882     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5883         return;
5885     gdouble value = -1;
5886     {
5887         gchar *endptr;
5888         gchar *const text = gtk_combo_box_get_active_text(cbox);
5889         if (text) {
5890             value = g_strtod(text, &endptr);
5891             if (endptr == text) {  // Conversion failed, non-numeric input.
5892                 value = -1;
5893             }
5894             g_free(text);
5895         }
5896     }
5897     if (value <= 0) {
5898         return; // could not parse value
5899     }
5901     SPCSSAttr *css = sp_repr_css_attr_new ();
5902     Inkscape::CSSOStringStream osfs;
5903     osfs << value;
5904     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5906     SPStyle *query =
5907         sp_style_new (SP_ACTIVE_DOCUMENT);
5908     int result_numbers =
5909         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5911     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5912     if (result_numbers == QUERY_STYLE_NOTHING)
5913     {
5914         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5915     }
5917     sp_style_unref(query);
5919     sp_desktop_set_style (desktop, css, true, true);
5920     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5921                                    _("Text: Change font size"));
5922     sp_repr_css_attr_unref (css);
5924     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5927 gboolean
5928 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5930     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5931     if (!desktop) return FALSE;
5933     if (!g_object_get_data (tbl, "esc-pressed")) {
5934         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5935         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5936         sp_text_toolbox_size_changed (cbox, tbl);
5937         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5938     }
5939     return FALSE; // I consumed the event
5943 gboolean
5944 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5946     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5947     if (!desktop) return FALSE;
5949     switch (get_group0_keyval (event)) {
5950         case GDK_Escape: // defocus
5951             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5952             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5953             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5954             return TRUE; // I consumed the event
5955             break;
5956         case GDK_Return: // defocus
5957         case GDK_KP_Enter:
5958             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5959             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5960             sp_text_toolbox_size_changed (cbox, tbl);
5961             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5962             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5963             return TRUE; // I consumed the event
5964             break;
5965     }
5966     return FALSE;
5969 void
5970 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5971                                          GObject            *tbl)
5973     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5974     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5975     int x, y;
5977     if (!popdown_visible)
5978     {
5979         gdk_window_get_origin (widget->window, &x, &y);
5980         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5981         gtk_widget_show_all (popdown);
5982         //sp_transientize (popdown);
5984         gdk_pointer_grab (widget->window, TRUE,
5985                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5986                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5987                                         GDK_POINTER_MOTION_MASK),
5988                           NULL, NULL, GDK_CURRENT_TIME);
5990         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5992         popdown_visible = true;
5993     }
5994     else
5995     {
5996         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5997         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5998         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5999         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6000         gtk_widget_hide (popdown);
6001         popdown_visible = false;
6002     }
6005 gboolean
6006 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6007                                  GdkEventFocus    */*event*/,
6008                                  GObject          */*tbl*/)
6010     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6011     return FALSE;
6014 gboolean
6015 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
6016                                    GdkEventFocus    */*event*/,
6017                                    GObject          */*tbl*/)
6019     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6021     if (popdown_hasfocus) {
6022         gtk_widget_hide (popdown);
6023         popdown_hasfocus = false;
6024         popdown_visible = false;
6025         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6026         return TRUE;
6027     }
6028     return FALSE;
6031 gboolean
6032 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
6033                                    GdkEventFocus    */*event*/,
6034                                    GObject          */*tbl*/)
6036     popdown_hasfocus = true;
6037     return TRUE;
6041 void
6042 cell_data_func  (GtkTreeViewColumn */*column*/,
6043                  GtkCellRenderer   *cell,
6044                  GtkTreeModel      *tree_model,
6045                  GtkTreeIter       *iter,
6046                  gpointer           /*data*/)
6048     gchar *family;
6049     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6050     gchar *const family_escaped = g_markup_escape_text(family, -1);
6052     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6053     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6055     std::stringstream markup;
6056     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6057            << family_escaped << "'>" << sample_escaped << "</span>";
6058     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6060     g_free(family);
6061     g_free(family_escaped);
6062     g_free(sample_escaped);
6065 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6066     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6067     if (completion) {
6068         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6069         g_object_unref (completion);
6070     }
6073 GtkWidget*
6074 sp_text_toolbox_new (SPDesktop *desktop)
6076     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6077     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
6079     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6080     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6082     GtkTooltips *tt = gtk_tooltips_new();
6083     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6085     ////////////Family
6086     //Window
6087     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6088     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6090     //Entry
6091     GtkWidget           *entry = gtk_entry_new ();
6092     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6093     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6094     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6095     gtk_entry_completion_set_text_column (completion, 0);
6096     gtk_entry_completion_set_minimum_key_length (completion, 1);
6097     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6098     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6099     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6100     gtk_toolbar_append_widget( tbl, entry, "", "" );
6101     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6103     //Button
6104     GtkWidget   *button = gtk_button_new ();
6105     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6106     gtk_toolbar_append_widget( tbl, button, "", "");
6108     //Popdown
6109     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6110     GtkWidget           *treeview = gtk_tree_view_new ();
6112     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6113     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6114     gtk_tree_view_column_pack_start (column, cell, FALSE);
6115     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6116     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6117     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6119     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6120     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6121     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6123     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6125     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6126     gtk_container_add (GTK_CONTAINER (sw), treeview);
6128     gtk_container_add (GTK_CONTAINER (window), sw);
6129     gtk_widget_set_size_request (window, 300, 450);
6131     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6132     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6133     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6135     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6137     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6138     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6139     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6141     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6142     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6144     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6145     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6146     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6147     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6148     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6150     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6151     GtkWidget *box = gtk_event_box_new ();
6152     gtk_container_add (GTK_CONTAINER (box), image);
6153     gtk_toolbar_append_widget( tbl, box, "", "");
6154     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6155     GtkTooltips *tooltips = gtk_tooltips_new ();
6156     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6157     gtk_widget_hide (GTK_WIDGET (box));
6158     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6160     ////////////Size
6161     gchar const *const sizes[] = {
6162         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6163         "16", "18", "20", "22", "24", "28",
6164         "32", "36", "40", "48", "56", "64", "72", "144"
6165     };
6167     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6168     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6169         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6170     }
6171     gtk_widget_set_size_request (cbox, 80, -1);
6172     gtk_toolbar_append_widget( tbl, cbox, "", "");
6173     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6174     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6175     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6176     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6178     ////////////Text anchor
6179     GtkWidget *group   = gtk_radio_button_new (NULL);
6180     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6181     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6183     // left
6184     GtkWidget *rbutton = group;
6185     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6186     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6187     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6189     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6190     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6191     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6192     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6194     // center
6195     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6196     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6197     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6198     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6200     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6201     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6202     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6203     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6205     // right
6206     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6207     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6208     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6209     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6211     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6212     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6213     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6214     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6216     // fill
6217     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6218     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6219     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6220     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6222     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6223     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6224     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6225     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6227     gtk_toolbar_append_widget( tbl, row, "", "");
6229     //spacer
6230     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6232     ////////////Text style
6233     row = gtk_hbox_new (FALSE, 4);
6235     // bold
6236     rbutton = gtk_toggle_button_new ();
6237     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6238     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6239     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6240     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6242     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6243     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6244     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6246     // italic
6247     rbutton = gtk_toggle_button_new ();
6248     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6249     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6250     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6251     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6253     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6254     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6255     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6257     gtk_toolbar_append_widget( tbl, row, "", "");
6259     //spacer
6260     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6262     ////////////Text orientation
6263     group   = gtk_radio_button_new (NULL);
6264     row     = gtk_hbox_new (FALSE, 4);
6265     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6267     // horizontal
6268     rbutton = group;
6269     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6270     gtk_container_add           (GTK_CONTAINER (rbutton),
6271                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6272     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6273     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6275     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6276     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6277     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6279     // vertical
6280     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6281     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6282     gtk_container_add           (GTK_CONTAINER (rbutton),
6283                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6284     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6285     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6287     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6288     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6289     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6290     gtk_toolbar_append_widget( tbl, row, "", "" );
6293     //watch selection
6294     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6296     sigc::connection *c_selection_changed =
6297         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6298                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6299     pool->add_connection ("selection-changed", c_selection_changed);
6301     sigc::connection *c_selection_modified =
6302         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6303                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6304     pool->add_connection ("selection-modified", c_selection_modified);
6306     sigc::connection *c_subselection_changed =
6307         new sigc::connection (desktop->connectToolSubselectionChanged
6308                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6309     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6311     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6314     gtk_widget_show_all( GTK_WIDGET(tbl) );
6316     return GTK_WIDGET(tbl);
6317 } // end of sp_text_toolbox_new()
6319 }//<unnamed> namespace
6322 //#########################
6323 //##      Connector      ##
6324 //#########################
6326 static void sp_connector_path_set_avoid(void)
6328     cc_selection_set_avoid(true);
6332 static void sp_connector_path_set_ignore(void)
6334     cc_selection_set_avoid(false);
6339 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6341     // quit if run by the _changed callbacks
6342     if (g_object_get_data( tbl, "freeze" )) {
6343         return;
6344     }
6346     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6347     SPDocument *doc = sp_desktop_document(desktop);
6349     if (!sp_document_get_undo_sensitive(doc))
6350     {
6351         return;
6352     }
6354     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6356     if ( repr->attribute("inkscape:connector-spacing") ) {
6357         gdouble priorValue = gtk_adjustment_get_value(adj);
6358         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6359         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6360             return;
6361         }
6362     } else if ( adj->value == defaultConnSpacing ) {
6363         return;
6364     }
6366     // in turn, prevent callbacks from responding
6367     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6369     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6370     SP_OBJECT(desktop->namedview)->updateRepr();
6372     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6373     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6374         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6375         NR::Matrix m = NR::identity();
6376         avoid_item_move(&m, item);
6377     }
6379     if (items) {
6380         g_slist_free(items);
6381     }
6383     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6384             _("Change connector spacing"));
6386     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6388     spinbutton_defocus(GTK_OBJECT(tbl));
6391 static void sp_connector_graph_layout(void)
6393     if (!SP_ACTIVE_DESKTOP) return;
6394     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6396     // hack for clones, see comment in align-and-distribute.cpp
6397     int saved_compensation = prefs->getInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6398     prefs->setInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6400     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6402     prefs->setInt("options.clonecompensation", "value", saved_compensation);
6404     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6407 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6409         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6410     prefs->setBool("tools.connector", "directedlayout",
6411                 gtk_toggle_action_get_active( act ));
6414 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6416         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6417     prefs->setBool("tools.connector", "avoidoverlaplayout",
6418                 gtk_toggle_action_get_active( act ));
6422 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6424         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6425     prefs->setDouble("tools.connector", "length", adj->value);
6426     spinbutton_defocus(GTK_OBJECT(tbl));
6429 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6430                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6431                                             bool /*is_interactive*/, gpointer data)
6433     GtkWidget *tbl = GTK_WIDGET(data);
6435     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6436         return;
6437     }
6438     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6439         return;
6440     }
6442     GtkAdjustment *adj = (GtkAdjustment*)
6443             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6444     gdouble spacing = defaultConnSpacing;
6445     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6447     gtk_adjustment_set_value(adj, spacing);
6451 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6452     NULL, /* child_added */
6453     NULL, /* child_removed */
6454     connector_tb_event_attr_changed,
6455     NULL, /* content_changed */
6456     NULL  /* order_changed */
6457 };
6460 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6462         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6463     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6465     {
6466         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6467                                           _("Avoid"),
6468                                           _("Make connectors avoid selected objects"),
6469                                           "connector_avoid",
6470                                           secondarySize );
6471         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6472         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6473     }
6475     {
6476         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6477                                           _("Ignore"),
6478                                           _("Make connectors ignore selected objects"),
6479                                           "connector_ignore",
6480                                           secondarySize );
6481         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6482         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6483     }
6485     EgeAdjustmentAction* eact = 0;
6487     // Spacing spinbox
6488     eact = create_adjustment_action( "ConnectorSpacingAction",
6489                                      _("Connector Spacing"), _("Spacing:"),
6490                                      _("The amount of space left around objects by auto-routing connectors"),
6491                                      "tools.connector", "spacing", defaultConnSpacing,
6492                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6493                                      0, 100, 1.0, 10.0,
6494                                      0, 0, 0,
6495                                      connector_spacing_changed, 1, 0 );
6496     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6498     // Graph (connector network) layout
6499     {
6500         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6501                                           _("Graph"),
6502                                           _("Nicely arrange selected connector network"),
6503                                           "graph_layout",
6504                                           secondarySize );
6505         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6506         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6507     }
6509     // Default connector length spinbox
6510     eact = create_adjustment_action( "ConnectorLengthAction",
6511                                      _("Connector Length"), _("Length:"),
6512                                      _("Ideal length for connectors when layout is applied"),
6513                                      "tools.connector", "length", 100,
6514                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6515                                      10, 1000, 10.0, 100.0,
6516                                      0, 0, 0,
6517                                      connector_length_changed, 1, 0 );
6518     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6521     // Directed edges toggle button
6522     {
6523         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6524                                                       _("Downwards"),
6525                                                       _("Make connectors with end-markers (arrows) point downwards"),
6526                                                       "directed_graph",
6527                                                       Inkscape::ICON_SIZE_DECORATION );
6528         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6530         bool tbuttonstate = prefs->getBool("tools.connector", "directedlayout");
6531         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
6533         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6534     }
6536     // Avoid overlaps toggle button
6537     {
6538         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6539                                                       _("Remove overlaps"),
6540                                                       _("Do not allow overlapping shapes"),
6541                                                       "remove_overlaps",
6542                                                       Inkscape::ICON_SIZE_DECORATION );
6543         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6545         bool tbuttonstate = prefs->getBool("tools.connector", "avoidoverlaplayout");
6546         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
6548         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6549     }
6551     // Code to watch for changes to the connector-spacing attribute in
6552     // the XML.
6553     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6554     g_assert(repr != NULL);
6556     purge_repr_listener( holder, holder );
6558     if (repr) {
6559         g_object_set_data( holder, "repr", repr );
6560         Inkscape::GC::anchor(repr);
6561         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6562         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6563     }
6564 } // end of sp_connector_toolbox_prep()
6567 //#########################
6568 //##     Paintbucket     ##
6569 //#########################
6571 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6573     gint channels = ege_select_one_action_get_active( act );
6574     flood_channels_set_channels( channels );
6577 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6579         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6580     prefs->setInt("tools.paintbucket", "threshold", (gint)adj->value);
6583 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6585         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6586     prefs->setInt("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6589 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6591     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6592     SPUnit const *unit = tracker->getActiveUnit();
6593     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6595     prefs->setDouble("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6596     prefs->setString("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6599 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6601     // FIXME: make defaults settable via Inkscape Options
6602     struct KeyValue {
6603         char const *key;
6604         double value;
6605     } const key_values[] = {
6606         {"threshold", 15},
6607         {"offset", 0.0}
6608     };
6610     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6611         KeyValue const &kv = key_values[i];
6612         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6613         if ( adj ) {
6614             gtk_adjustment_set_value(adj, kv.value);
6615         }
6616     }
6618     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6619     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6620     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6621     ege_select_one_action_set_active( autogap_action, 0 );
6624 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6626     EgeAdjustmentAction* eact = 0;
6627     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6629     {
6630         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6632         GList* items = 0;
6633         gint count = 0;
6634         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6635         {
6636             GtkTreeIter iter;
6637             gtk_list_store_append( model, &iter );
6638             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6639             count++;
6640         }
6641         g_list_free( items );
6642         items = 0;
6643         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6644         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6645         ege_select_one_action_set_appearance( act1, "compact" );
6646         ege_select_one_action_set_active( act1, prefs->getInt("tools.paintbucket", "channels", 0) );
6647         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6648         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6649         g_object_set_data( holder, "channels_action", act1 );
6650     }
6652     // Spacing spinbox
6653     {
6654         eact = create_adjustment_action(
6655             "ThresholdAction",
6656             _("Fill Threshold"), _("Threshold:"),
6657             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6658             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6659             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6660             0, 0, 0,
6661             paintbucket_threshold_changed, 1, 0 );
6663         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6664         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6665     }
6667     // Create the units menu.
6668     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6669     Glib::ustring stored_unit = prefs->getString("tools.paintbucket", "offsetunits");
6670     if (!stored_unit.empty())
6671         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
6672     g_object_set_data( holder, "tracker", tracker );
6673     {
6674         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6675         gtk_action_group_add_action( mainActions, act );
6676     }
6678     // Offset spinbox
6679     {
6680         eact = create_adjustment_action(
6681             "OffsetAction",
6682             _("Grow/shrink by"), _("Grow/shrink by:"),
6683             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6684             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6685             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6686             0, 0, 0,
6687             paintbucket_offset_changed, 1, 2);
6688         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6690         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6691     }
6693     /* Auto Gap */
6694     {
6695         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6697         GList* items = 0;
6698         gint count = 0;
6699         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6700         {
6701             GtkTreeIter iter;
6702             gtk_list_store_append( model, &iter );
6703             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6704             count++;
6705         }
6706         g_list_free( items );
6707         items = 0;
6708         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6709         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6710         ege_select_one_action_set_appearance( act2, "compact" );
6711         ege_select_one_action_set_active( act2, prefs->getInt("tools.paintbucket", "autogap", 0) );
6712         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6713         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6714         g_object_set_data( holder, "autogap_action", act2 );
6715     }
6717     /* Reset */
6718     {
6719         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6720                                           _("Defaults"),
6721                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6722                                           GTK_STOCK_CLEAR );
6723         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6724         gtk_action_group_add_action( mainActions, act );
6725         gtk_action_set_sensitive( act, TRUE );
6726     }
6730 /*
6731   Local Variables:
6732   mode:c++
6733   c-file-style:"stroustrup"
6734   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6735   indent-tabs-mode:nil
6736   fill-column:99
6737   End:
6738 */
6739 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :