Code

Merging from trunk
[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");
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, _("Move mode"),
3696                             1, _("Move objects in any direction"),
3697                             2, "tweak_move_mode",
3698                             -1 );
3700         gtk_list_store_append( model, &iter );
3701         gtk_list_store_set( model, &iter,
3702                             0, _("Move in/out mode"),
3703                             1, _("Move objects towards cursor; with Shift from cursor"),
3704                             2, "tweak_move_mode_inout",
3705                             -1 );
3707         gtk_list_store_append( model, &iter );
3708         gtk_list_store_set( model, &iter,
3709                             0, _("Move jitter mode"),
3710                             1, _("Move objects in random directions"),
3711                             2, "tweak_move_mode_jitter",
3712                             -1 );
3714         gtk_list_store_append( model, &iter );
3715         gtk_list_store_set( model, &iter,
3716                             0, _("Scale mode"),
3717                             1, _("Scale objects, with Shift scale up"),
3718                             2, "tweak_scale_mode",
3719                             -1 );
3721         gtk_list_store_append( model, &iter );
3722         gtk_list_store_set( model, &iter,
3723                             0, _("Rotate mode"),
3724                             1, _("Rotate objects, with Shift counterclockwise"),
3725                             2, "tweak_rotate_mode",
3726                             -1 );
3728         gtk_list_store_append( model, &iter );
3729         gtk_list_store_set( model, &iter,
3730                             0, _("Duplicate/delete mode"),
3731                             1, _("Duplicate objects, with Shift delete"),
3732                             2, "tweak_moreless_mode",
3733                             -1 );
3735         gtk_list_store_append( model, &iter );
3736         gtk_list_store_set( model, &iter,
3737                             0, _("Push mode"),
3738                             1, _("Push parts of paths in any direction"),
3739                             2, "tweak_push_mode",
3740                             -1 );
3742         gtk_list_store_append( model, &iter );
3743         gtk_list_store_set( model, &iter,
3744                             0, _("Shrink/grow mode"),
3745                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
3746                             2, "tweak_shrink_mode",
3747                             -1 );
3749         gtk_list_store_append( model, &iter );
3750         gtk_list_store_set( model, &iter,
3751                             0, _("Attract/repel mode"),
3752                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
3753                             2, "tweak_attract_mode",
3754                             -1 );
3756         gtk_list_store_append( model, &iter );
3757         gtk_list_store_set( model, &iter,
3758                             0, _("Roughen mode"),
3759                             1, _("Roughen parts of paths"),
3760                             2, "tweak_roughen_mode",
3761                             -1 );
3763         gtk_list_store_append( model, &iter );
3764         gtk_list_store_set( model, &iter,
3765                             0, _("Color paint mode"),
3766                             1, _("Paint the tool's color upon selected objects"),
3767                             2, "tweak_colorpaint_mode",
3768                             -1 );
3770         gtk_list_store_append( model, &iter );
3771         gtk_list_store_set( model, &iter,
3772                             0, _("Color jitter mode"),
3773                             1, _("Jitter the colors of selected objects"),
3774                             2, "tweak_colorjitter_mode",
3775                             -1 );
3777         gtk_list_store_append( model, &iter );
3778         gtk_list_store_set( model, &iter,
3779                             0, _("Blur mode"),
3780                             1, _("Blur selected objects more; with Shift, blur less"),
3781                             2, "tweak_blur_mode",
3782                             -1 );
3785         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3786         g_object_set( act, "short_label", _("Mode:"), NULL );
3787         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3788         g_object_set_data( holder, "mode_action", act );
3790         ege_select_one_action_set_appearance( act, "full" );
3791         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3792         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3793         ege_select_one_action_set_icon_column( act, 2 );
3794         ege_select_one_action_set_icon_size( act, secondarySize );
3795         ege_select_one_action_set_tooltip_column( act, 1  );
3797         gint mode = prefs->getInt("tools.tweak", "mode", 0);
3798         ege_select_one_action_set_active( act, mode );
3799         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3801         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3802     }
3804     guint mode = prefs->getInt("tools.tweak", "mode", 0);
3806     {
3807         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3808         ege_output_action_set_use_markup( act, TRUE );
3809         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3810         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3811             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3812         g_object_set_data( holder, "tweak_channels_label", act);
3813     }
3815     {
3816         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3817                                                       _("Hue"),
3818                                                       _("In color mode, act on objects' hue"),
3819                                                       NULL,
3820                                                       Inkscape::ICON_SIZE_DECORATION );
3821         //TRANSLATORS:  "H" here stands for hue
3822         g_object_set( act, "short_label", _("H"), NULL );
3823         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3824         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3825         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doh", 1 ) );
3826         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3827             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3828         g_object_set_data( holder, "tweak_doh", act);
3829     }
3830     {
3831         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3832                                                       _("Saturation"),
3833                                                       _("In color mode, act on objects' saturation"),
3834                                                       NULL,
3835                                                       Inkscape::ICON_SIZE_DECORATION );
3836         //TRANSLATORS: "S" here stands for Saturation
3837         g_object_set( act, "short_label", _("S"), NULL );
3838         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3839         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3840         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dos", 1 ) );
3841         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3842             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3843         g_object_set_data( holder, "tweak_dos", act );
3844     }
3845     {
3846         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3847                                                       _("Lightness"),
3848                                                       _("In color mode, act on objects' lightness"),
3849                                                       NULL,
3850                                                       Inkscape::ICON_SIZE_DECORATION );
3851         //TRANSLATORS: "L" here stands for Lightness
3852         g_object_set( act, "short_label", _("L"), NULL );
3853         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3854         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3855         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "dol", 1 ) );
3856         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3857             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3858         g_object_set_data( holder, "tweak_dol", act );
3859     }
3860     {
3861         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3862                                                       _("Opacity"),
3863                                                       _("In color mode, act on objects' opacity"),
3864                                                       NULL,
3865                                                       Inkscape::ICON_SIZE_DECORATION );
3866         //TRANSLATORS: "O" here stands for Opacity
3867         g_object_set( act, "short_label", _("O"), NULL );
3868         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3869         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3870         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.tweak", "doo", 1 ) );
3871         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3872             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3873         g_object_set_data( holder, "tweak_doo", act );
3874     }
3876     {   /* Fidelity */
3877         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3878         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3879         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3880                                                               _("Fidelity"), _("Fidelity:"),
3881                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3882                                                               "tools.tweak", "fidelity", 50,
3883                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3884                                                               1, 100, 1.0, 10.0,
3885                                                               labels, values, G_N_ELEMENTS(labels),
3886                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3887         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3888         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3889         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3890             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3891         g_object_set_data( holder, "tweak_fidelity", eact );
3892     }
3895     /* Use Pressure button */
3896     {
3897         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3898                                                       _("Pressure"),
3899                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3900                                                       "use_pressure",
3901                                                       Inkscape::ICON_SIZE_DECORATION );
3902         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3903         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3904         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.tweak", "usepressure", true) );
3905     }
3910 //########################
3911 //##     Calligraphy    ##
3912 //########################
3913 static void update_presets_list (GObject *tbl)
3915     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3916     if (g_object_get_data(tbl, "presets_blocked"))
3917         return;
3919     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3920     if (!sel) {
3921         ege_select_one_action_set_active(sel, 0);
3922         return;
3923     }
3925     int total_prefs = prefs->childCount("tools.calligraphic.preset");
3927     for (int i = 0; i < total_prefs; ++i) {
3928         Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", i);
3929         /// @todo Remove the use of _getNode()
3930         Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
3932         bool match = true;
3934         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3935               iter;
3936               ++iter ) {
3937             const gchar *attr_name = g_quark_to_string(iter->key);
3938             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3939                 continue;
3940             void *widget = g_object_get_data(tbl, attr_name);
3941             if (widget) {
3942                 if (GTK_IS_ADJUSTMENT(widget)) {
3943                     double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3944                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3945                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3946                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3947                         match = false;
3948                         break;
3949                     }
3950                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3951                     bool v = prefs->getBool(preset_path, attr_name);
3952                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3953                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3954                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
3955                         match = false;
3956                         break;
3957                     }
3958                 }
3959             }
3960         }
3962         if (match) {
3963             // newly added item is at the same index as the
3964             // save command, so we need to change twice for it to take effect
3965             ege_select_one_action_set_active(sel, 0);
3966             ege_select_one_action_set_active(sel, i + 1); // one-based index
3967             return;
3968         }
3969     }
3971     // no match found
3972     ege_select_one_action_set_active(sel, 0);
3975 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3977     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3978     prefs->setDouble( "tools.calligraphic", "mass", adj->value * 0.01 );
3979     update_presets_list(tbl);
3982 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3984     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3985     prefs->setDouble( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3986     update_presets_list(tbl);
3989 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3991     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3992     prefs->setDouble( "tools.calligraphic", "angle", adj->value );
3993     update_presets_list(tbl);
3996 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3998     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3999     prefs->setDouble( "tools.calligraphic", "width", adj->value * 0.01 );
4000     update_presets_list(tbl);
4003 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4005     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4006     prefs->setDouble("tools.calligraphic", "thinning", adj->value * 0.01 );
4007     update_presets_list(tbl);
4010 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4012     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4013     prefs->setDouble( "tools.calligraphic", "flatness", adj->value * 0.01);
4014     update_presets_list(tbl);
4017 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4019     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4020     prefs->setDouble( "tools.calligraphic", "tremor", adj->value * 0.01 );
4021     update_presets_list(tbl);
4024 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4026     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4027     prefs->setDouble( "tools.calligraphic", "cap_rounding", adj->value );
4028     update_presets_list(tbl);
4031 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4033     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4034     prefs->setBool("tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ));
4035     update_presets_list(tbl);
4038 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4040     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4041     prefs->setBool("tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ));
4042     update_presets_list(tbl);
4045 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4047     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4048     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4049     prefs->setBool("tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ));
4050     update_presets_list(tbl);
4051     if (calligraphy_angle )
4052         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4056 static gchar const *const widget_names[] = {
4057     "width",
4058     "mass",
4059     "wiggle",
4060     "angle",
4061     "thinning",
4062     "tremor",
4063     "flatness",
4064     "cap_rounding",
4065     "usepressure",
4066     "tracebackground",
4067     "usetilt"
4068 };
4071 static void sp_dcc_build_presets_list(GObject *tbl)
4073     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4075     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4076     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4077     gtk_list_store_clear (model);
4079     {
4080         GtkTreeIter iter;
4081         gtk_list_store_append( model, &iter );
4082         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4083     }
4085     /// @todo Use public Preferences API instead of node manipulation
4086     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4087     Inkscape::XML::Node *repr = prefs->_getNode("tools.calligraphic.preset", true);
4088     Inkscape::XML::Node *child_repr = repr->firstChild();
4089     int ii=1;
4090     while (child_repr) {
4091         GtkTreeIter iter;
4092         char *preset_name = (char *) child_repr->attribute("name");
4093         gtk_list_store_append( model, &iter );
4094         gtk_list_store_set( model, &iter, 0, preset_name, 1, ii++, -1 );
4095         child_repr = child_repr->next();
4096     }
4098     {
4099         GtkTreeIter iter;
4100         gtk_list_store_append( model, &iter );
4101         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4102         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4103     }
4105     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4107     update_presets_list (tbl);
4110 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4112     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4113     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4114     if (! desktop) return;
4116     if (g_object_get_data(tbl, "presets_blocked"))
4117         return;
4119     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4120     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4121         // dialog cancelled
4122         update_presets_list (tbl);
4123         return;
4124     }
4125     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4127     if (profile_name.empty()) {
4128         // empty name entered
4129         update_presets_list (tbl);
4130         return;
4131     }
4133     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4135     int new_index = -1;
4136     Glib::ustring pref_path;
4137     int total_prefs = prefs->childCount("tools.calligraphic.preset");
4139     for (int i = 0; i < total_prefs; ++i) {
4140         Glib::ustring path = prefs->getNthChild("tools.calligraphic.preset", i);
4141         Glib::ustring name = prefs->getString(path, "name");
4142         if (!name.empty() && ( profile_name == name )) {
4143             // we already have preset with this name, replace its values
4144             new_index = i;
4145             pref_path = path;
4146             break;
4147         }
4148     }
4150     if (new_index == -1) {
4151         // no preset with this name, create
4152         /// @todo This is wrong, the name should be encoded in the key
4153         /// to allow deletion of presets
4154         new_index = total_prefs + 1;
4155         gchar *profile_id = g_strdup_printf(".dcc%d", new_index);
4156         pref_path = Glib::ustring("tools.calligraphic.preset") + profile_id;
4157         g_free(profile_id);
4158     }
4160     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4161         gchar const *const widget_name = widget_names[i];
4162         void *widget = g_object_get_data(tbl, widget_name);
4163         if (widget) {
4164             if (GTK_IS_ADJUSTMENT(widget)) {
4165                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4166                 double v = gtk_adjustment_get_value(adj);
4167                 prefs->setDouble(pref_path, widget_name, v);
4168                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4169             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4170                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4171                 bool v = gtk_toggle_action_get_active(toggle);
4172                 prefs->setBool(pref_path, widget_name, v);
4173                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4174             } else {
4175                g_warning("Unknown widget type for preset: %s\n", widget_name);
4176             }
4177         } else {
4178             g_warning("Bad key when writing preset: %s\n", widget_name);
4179         }
4180     }
4181     prefs->setString(pref_path, "name", profile_name);
4183     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4184     sp_dcc_build_presets_list (tbl);
4188 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4190     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4192     gint preset_index = ege_select_one_action_get_active( act );
4193     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4195     if (preset_index == save_presets_index) {
4196         // this is the Save command
4197         sp_dcc_save_profile(NULL, tbl);
4198         return;
4199     }
4201     if (g_object_get_data(tbl, "presets_blocked"))
4202         return;
4204     // preset_index is one-based so we subtract 1
4205     Glib::ustring preset_path = prefs->getNthChild("tools.calligraphic.preset", preset_index - 1);
4207     if (!preset_path.empty()) {
4208         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE)); //temporarily block the selector so no one will updadte it while we're reading it
4210         /// @todo Remove the use of _getNode() in this fragment, modify
4211         /// the public interface of Preferences if necessary
4212         Inkscape::XML::Node *preset_repr = prefs->_getNode(preset_path);
4214         // Shouldn't this be std::map?
4215         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4216               iter;
4217               ++iter ) {
4218             const gchar *attr_name = g_quark_to_string(iter->key);
4219             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4220                 continue;
4221             void *widget = g_object_get_data(tbl, attr_name);
4222             if (widget) {
4223                 if (GTK_IS_ADJUSTMENT(widget)) {
4224                     double v = prefs->getDouble(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4225                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4226                     gtk_adjustment_set_value(adj, v);
4227                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4228                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4229                     int v = prefs->getInt(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4230                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4231                     gtk_toggle_action_set_active(toggle, v);
4232                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4233                 } else {
4234                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4235                 }
4236             } else {
4237                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4238             }
4239         }
4240         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4241     }
4246 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4248     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4249     {
4250         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4252         EgeAdjustmentAction* calligraphy_angle = 0;
4254         {
4255         /* Width */
4256         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4257         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4258         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4259                                                               _("Pen Width"), _("Width:"),
4260                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4261                                                               "tools.calligraphic", "width", 15,
4262                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4263                                                               1, 100, 1.0, 0.0,
4264                                                               labels, values, G_N_ELEMENTS(labels),
4265                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4266         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4267         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4268         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4269         }
4271         {
4272         /* Thinning */
4273             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4274             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4275         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4276                                                               _("Stroke Thinning"), _("Thinning:"),
4277                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4278                                                               "tools.calligraphic", "thinning", 10,
4279                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4280                                                               -100, 100, 1, 0.1,
4281                                                               labels, values, G_N_ELEMENTS(labels),
4282                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4283         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4284         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4285         }
4287         {
4288         /* Angle */
4289         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4290         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4291         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4292                                                               _("Pen Angle"), _("Angle:"),
4293                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4294                                                               "tools.calligraphic", "angle", 30,
4295                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4296                                                               -90.0, 90.0, 1.0, 10.0,
4297                                                               labels, values, G_N_ELEMENTS(labels),
4298                                                               sp_ddc_angle_value_changed, 1, 0 );
4299         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300         g_object_set_data( holder, "angle_action", eact );
4301         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4302         calligraphy_angle = eact;
4303         }
4305         {
4306         /* Fixation */
4307             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4308         gdouble values[] = {0, 20, 40, 60, 90, 100};
4309         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4310                                                               _("Fixation"), _("Fixation:"),
4311                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4312                                                               "tools.calligraphic", "flatness", 90,
4313                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4314                                                               0.0, 100, 1.0, 10.0,
4315                                                               labels, values, G_N_ELEMENTS(labels),
4316                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4317         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4318         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4319         }
4321         {
4322         /* Cap Rounding */
4323             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4324         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4325         // TRANSLATORS: "cap" means "end" (both start and finish) here
4326         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4327                                                               _("Cap rounding"), _("Caps:"),
4328                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4329                                                               "tools.calligraphic", "cap_rounding", 0.0,
4330                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4331                                                               0.0, 5.0, 0.01, 0.1,
4332                                                               labels, values, G_N_ELEMENTS(labels),
4333                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4334         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4335         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4336         }
4338         {
4339         /* Tremor */
4340             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4341         gdouble values[] = {0, 10, 20, 40, 60, 100};
4342         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4343                                                               _("Stroke Tremor"), _("Tremor:"),
4344                                                               _("Increase to make strokes rugged and trembling"),
4345                                                               "tools.calligraphic", "tremor", 0.0,
4346                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4347                                                               0.0, 100, 1, 0.0,
4348                                                               labels, values, G_N_ELEMENTS(labels),
4349                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4351         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4352         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4353         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4354         }
4356         {
4357         /* Wiggle */
4358         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4359         gdouble values[] = {0, 20, 40, 60, 100};
4360         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4361                                                               _("Pen Wiggle"), _("Wiggle:"),
4362                                                               _("Increase to make the pen waver and wiggle"),
4363                                                               "tools.calligraphic", "wiggle", 0.0,
4364                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4365                                                               0.0, 100, 1, 0.0,
4366                                                               labels, values, G_N_ELEMENTS(labels),
4367                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4368         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4369         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4370         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4371         }
4373         {
4374         /* Mass */
4375             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4376         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4377         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4378                                                               _("Pen Mass"), _("Mass:"),
4379                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4380                                                               "tools.calligraphic", "mass", 2.0,
4381                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4382                                                               0.0, 100, 1, 0.0,
4383                                                               labels, values, G_N_ELEMENTS(labels),
4384                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4385         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4386         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4387         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4388         }
4391         /* Trace Background button */
4392         {
4393             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4394                                                           _("Trace Background"),
4395                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4396                                                           "trace_background",
4397                                                           Inkscape::ICON_SIZE_DECORATION );
4398             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4399             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4400             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "tracebackground", false) );
4401             g_object_set_data( holder, "tracebackground", act );
4402         }
4404         /* Use Pressure button */
4405         {
4406             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4407                                                           _("Pressure"),
4408                                                           _("Use the pressure of the input device to alter the width of the pen"),
4409                                                           "use_pressure",
4410                                                           Inkscape::ICON_SIZE_DECORATION );
4411             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4412             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4413             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usepressure", true) );
4414             g_object_set_data( holder, "usepressure", act );
4415         }
4417         /* Use Tilt button */
4418         {
4419             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4420                                                           _("Tilt"),
4421                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4422                                                           "use_tilt",
4423                                                           Inkscape::ICON_SIZE_DECORATION );
4424             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4425             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4426             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("tools.calligraphic", "usetilt", true) );
4427             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("tools.calligraphic", "usetilt", true) );
4428             g_object_set_data( holder, "usetilt", act );
4429         }
4431         /*calligraphic profile */
4432         {
4433             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4434             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4435             ege_select_one_action_set_appearance (act1, "compact");
4436             g_object_set_data (holder, "profile_selector", act1 );
4438             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4440             sp_dcc_build_presets_list (holder);
4442             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4443             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4444         }
4445     }
4449 //########################
4450 //##    Circle / Arc    ##
4451 //########################
4453 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4455     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4456     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4458     if (v1 == 0 && v2 == 0) {
4459         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4460             gtk_action_set_sensitive( ocb, FALSE );
4461             gtk_action_set_sensitive( make_whole, FALSE );
4462         }
4463     } else {
4464         gtk_action_set_sensitive( ocb, TRUE );
4465         gtk_action_set_sensitive( make_whole, TRUE );
4466     }
4469 static void
4470 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4472     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4474     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4475         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4476         prefs->setDouble("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4477     }
4479     // quit if run by the attr_changed listener
4480     if (g_object_get_data( tbl, "freeze" )) {
4481         return;
4482     }
4484     // in turn, prevent listener from responding
4485     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4487     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4489     bool modmade = false;
4490     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4491          items != NULL;
4492          items = items->next)
4493     {
4494         SPItem *item = SP_ITEM(items->data);
4496         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4498             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4499             SPArc *arc = SP_ARC(item);
4501             if (!strcmp(value_name, "start"))
4502                 ge->start = (adj->value * M_PI)/ 180;
4503             else
4504                 ge->end = (adj->value * M_PI)/ 180;
4506             sp_genericellipse_normalize(ge);
4507             ((SPObject *)arc)->updateRepr();
4508             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4510             modmade = true;
4511         }
4512     }
4514     g_free(namespaced_name);
4516     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4518     sp_arctb_sensitivize( tbl, adj->value, other->value );
4520     if (modmade) {
4521         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4522                                    _("Arc: Change start/end"));
4523     }
4525     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4529 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4531     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4534 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4536     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4540 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4542     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4543     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4544         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4545         prefs->setBool("tools.shapes.arc", "open", ege_select_one_action_get_active(act) != 0);
4546     }
4548     // quit if run by the attr_changed listener
4549     if (g_object_get_data( tbl, "freeze" )) {
4550         return;
4551     }
4553     // in turn, prevent listener from responding
4554     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4556     bool modmade = false;
4558     if ( ege_select_one_action_get_active(act) != 0 ) {
4559         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4560              items != NULL;
4561              items = items->next)
4562         {
4563             if (SP_IS_ARC((SPItem *) items->data)) {
4564                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4565                 repr->setAttribute("sodipodi:open", "true");
4566                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4567                 modmade = true;
4568             }
4569         }
4570     } else {
4571         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4572              items != NULL;
4573              items = items->next)
4574         {
4575             if (SP_IS_ARC((SPItem *) items->data))    {
4576                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4577                 repr->setAttribute("sodipodi:open", NULL);
4578                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4579                 modmade = true;
4580             }
4581         }
4582     }
4584     if (modmade) {
4585         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4586                                    _("Arc: Change open/closed"));
4587     }
4589     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4592 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4594     GtkAdjustment *adj;
4595     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4596     gtk_adjustment_set_value(adj, 0.0);
4597     gtk_adjustment_value_changed(adj);
4599     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4600     gtk_adjustment_set_value(adj, 0.0);
4601     gtk_adjustment_value_changed(adj);
4603     spinbutton_defocus( GTK_OBJECT(obj) );
4606 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4607                                       gchar const */*old_value*/, gchar const */*new_value*/,
4608                                       bool /*is_interactive*/, gpointer data)
4610     GObject *tbl = G_OBJECT(data);
4612     // quit if run by the _changed callbacks
4613     if (g_object_get_data( tbl, "freeze" )) {
4614         return;
4615     }
4617     // in turn, prevent callbacks from responding
4618     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4620     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4621     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4623     GtkAdjustment *adj1,*adj2;
4624     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4625     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4626     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4627     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4629     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4631     char const *openstr = NULL;
4632     openstr = repr->attribute("sodipodi:open");
4633     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4635     if (openstr) {
4636         ege_select_one_action_set_active( ocb, 1 );
4637     } else {
4638         ege_select_one_action_set_active( ocb, 0 );
4639     }
4641     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4644 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4645     NULL, /* child_added */
4646     NULL, /* child_removed */
4647     arc_tb_event_attr_changed,
4648     NULL, /* content_changed */
4649     NULL  /* order_changed */
4650 };
4653 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4655     int n_selected = 0;
4656     Inkscape::XML::Node *repr = NULL;
4658     purge_repr_listener( tbl, tbl );
4660     for (GSList const *items = selection->itemList();
4661          items != NULL;
4662          items = items->next)
4663     {
4664         if (SP_IS_ARC((SPItem *) items->data)) {
4665             n_selected++;
4666             repr = SP_OBJECT_REPR((SPItem *) items->data);
4667         }
4668     }
4670     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4672     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4673     if (n_selected == 0) {
4674         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4675     } else if (n_selected == 1) {
4676         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4677         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4679         if (repr) {
4680             g_object_set_data( tbl, "repr", repr );
4681             Inkscape::GC::anchor(repr);
4682             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4683             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4684         }
4685     } else {
4686         // FIXME: implement averaging of all parameters for multiple selected
4687         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4688         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4689         sp_arctb_sensitivize( tbl, 1, 0 );
4690     }
4694 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4696     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4698     EgeAdjustmentAction* eact = 0;
4699     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4702     {
4703         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4704         ege_output_action_set_use_markup( act, TRUE );
4705         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4706         g_object_set_data( holder, "mode_action", act );
4707     }
4709     /* Start */
4710     {
4711         eact = create_adjustment_action( "ArcStartAction",
4712                                          _("Start"), _("Start:"),
4713                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4714                                          "tools.shapes.arc", "start", 0.0,
4715                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4716                                          -360.0, 360.0, 1.0, 10.0,
4717                                          0, 0, 0,
4718                                          sp_arctb_start_value_changed);
4719         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4720     }
4722     /* End */
4723     {
4724         eact = create_adjustment_action( "ArcEndAction",
4725                                          _("End"), _("End:"),
4726                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4727                                          "tools.shapes.arc", "end", 0.0,
4728                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4729                                          -360.0, 360.0, 1.0, 10.0,
4730                                          0, 0, 0,
4731                                          sp_arctb_end_value_changed);
4732         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4733     }
4735     /* Segments / Pie checkbox */
4736     {
4737         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4739         GtkTreeIter iter;
4740         gtk_list_store_append( model, &iter );
4741         gtk_list_store_set( model, &iter,
4742                             0, _("Closed arc"),
4743                             1, _("Switch to segment (closed shape with two radii)"),
4744                             2, "circle_closed_arc",
4745                             -1 );
4747         gtk_list_store_append( model, &iter );
4748         gtk_list_store_set( model, &iter,
4749                             0, _("Open Arc"),
4750                             1, _("Switch to arc (unclosed shape)"),
4751                             2, "circle_open_arc",
4752                             -1 );
4754         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4755         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4756         g_object_set_data( holder, "open_action", act );
4758         ege_select_one_action_set_appearance( act, "full" );
4759         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4760         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4761         ege_select_one_action_set_icon_column( act, 2 );
4762         ege_select_one_action_set_icon_size( act, secondarySize );
4763         ege_select_one_action_set_tooltip_column( act, 1  );
4765         bool isClosed = !prefs->getBool("tools.shapes.arc", "open", false);
4766         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4767         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4768     }
4770     /* Make Whole */
4771     {
4772         InkAction* inky = ink_action_new( "ArcResetAction",
4773                                           _("Make whole"),
4774                                           _("Make the shape a whole ellipse, not arc or segment"),
4775                                           "reset_circle",
4776                                           secondarySize );
4777         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4778         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4779         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4780         g_object_set_data( holder, "make_whole", inky );
4781     }
4783     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4784     // sensitivize make whole and open checkbox
4785     {
4786         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4787         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4788         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4789     }
4792     sigc::connection *connection = new sigc::connection(
4793         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4794         );
4795     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4796     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4802 // toggle button callbacks and updaters
4804 //########################
4805 //##      Dropper       ##
4806 //########################
4808 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4809     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4810     prefs->setInt( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4811     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4812     if ( set_action ) {
4813         if ( gtk_toggle_action_get_active( act ) ) {
4814             gtk_action_set_sensitive( set_action, TRUE );
4815         } else {
4816             gtk_action_set_sensitive( set_action, FALSE );
4817         }
4818     }
4820     spinbutton_defocus(GTK_OBJECT(tbl));
4823 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4824     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4825     prefs->setInt( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4826     spinbutton_defocus(GTK_OBJECT(tbl));
4830 /**
4831  * Dropper auxiliary toolbar construction and setup.
4832  *
4833  * TODO: Would like to add swatch of current color.
4834  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4835  *       can drag and drop places. Will provide a nice mixing palette.
4836  */
4837 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4839     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4840     gint pickAlpha = prefs->getInt( "tools.dropper", "pick", 1 );
4842     {
4843         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4844         ege_output_action_set_use_markup( act, TRUE );
4845         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4846     }
4848     {
4849         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4850                                                       _("Pick opacity"),
4851                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4852                                                       NULL,
4853                                                       Inkscape::ICON_SIZE_DECORATION );
4854         g_object_set( act, "short_label", _("Pick"), NULL );
4855         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4856         g_object_set_data( holder, "pick_action", act );
4857         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4858         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4859     }
4861     {
4862         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4863                                                       _("Assign opacity"),
4864                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4865                                                       NULL,
4866                                                       Inkscape::ICON_SIZE_DECORATION );
4867         g_object_set( act, "short_label", _("Assign"), NULL );
4868         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4869         g_object_set_data( holder, "set_action", act );
4870         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.dropper", "setalpha", 1 ) );
4871         // make sure it's disabled if we're not picking alpha
4872         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4873         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4874     }
4878 //########################
4879 //##      LPETool       ##
4880 //########################
4882 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4884 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4885 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4887     using namespace Inkscape::LivePathEffect;
4889     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4890     SPEventContext *ec = desktop->event_context;
4891     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4892         return;
4893     }
4895     // only take action if run by the attr_changed listener
4896     if (!g_object_get_data(tbl, "freeze")) {
4897         // in turn, prevent listener from responding
4898         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4900         gint mode = ege_select_one_action_get_active(act);
4901         EffectType type = lpesubtools[mode];
4903         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4904         bool success = lpetool_try_construction(lc, type);
4905         if (success) {
4906             // since the construction was already performed, we set the state back to inactive
4907             ege_select_one_action_set_active(act, 0);
4908             mode = 0;
4909         } else {
4910             // switch to the chosen subtool
4911             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4912         }
4914         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4915             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4916             prefs->setInt( "tools.lpetool", "mode", mode );
4917         }
4919         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4920     }
4923 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
4925     SPEventContext *ec = selection->desktop()->event_context;
4926     if (!SP_IS_LPETOOL_CONTEXT(ec))
4927         return;
4929     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4932 void
4933 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4935     using namespace Inkscape::LivePathEffect;
4936     SPEventContext *ec = selection->desktop()->event_context;
4937     if (!SP_IS_LPETOOL_CONTEXT(ec))
4938         return;
4939     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4941     lpetool_delete_measuring_items(lc);
4942     lpetool_create_measuring_items(lc, selection);
4944     // activate line segment combo box if a single item with LPELineSegment is selected
4945     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4946     SPItem *item = selection->singleItem();
4947     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4948         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4949         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4950         if (lpe && lpe->effectType() == LINE_SEGMENT) {
4951             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4952             g_object_set_data(tbl, "currentlpe", lpe);
4953             g_object_set_data(tbl, "currentlpeitem", lpeitem);
4954             gtk_action_set_sensitive(w, TRUE);
4955             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4956         } else {
4957             g_object_set_data(tbl, "currentlpe", NULL);
4958             g_object_set_data(tbl, "currentlpeitem", NULL);
4959             gtk_action_set_sensitive(w, FALSE);
4960         }
4961     } else {
4962         g_object_set_data(tbl, "currentlpe", NULL);
4963         g_object_set_data(tbl, "currentlpeitem", NULL);
4964         gtk_action_set_sensitive(w, FALSE);
4965     }
4968 static void
4969 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4970     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4971     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4973     bool show = gtk_toggle_action_get_active( act );
4974     prefs->setInt("tools.lpetool", "show_bbox",  show ? 1 : 0);
4976     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4977         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4978         lpetool_context_reset_limiting_bbox(lc);
4979     }
4982 static void
4983 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
4984     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
4985     if (!tools_isactive(desktop, TOOLS_LPETOOL))
4986         return;
4988     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
4989     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4990     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4991         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4992         bool show = gtk_toggle_action_get_active( act );
4993         prefs->setInt("tools.lpetool", "show_measuring_info",  show ? 1 : 0);
4994         lpetool_show_measuring_info(lc, show);
4995         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
4996     }
4999 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5000     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5001     SPUnit const *unit = tracker->getActiveUnit();
5002     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5003     prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
5005     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5006     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5007         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5008         lpetool_delete_measuring_items(lc);
5009         lpetool_create_measuring_items(lc);
5010     }
5013 static void
5014 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5015     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5016     Inkscape::Selection *selection = desktop->selection;
5018     boost::optional<Geom::Rect> bbox = selection->bounds();
5020     if (bbox) {
5021         Geom::Point A(bbox->min());
5022         Geom::Point B(bbox->max());
5024         A *= desktop->doc2dt();
5025         B *= desktop->doc2dt();
5027         // TODO: should we provide a way to store points in prefs?
5028         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5029         prefs->setDouble("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
5030         prefs->setDouble("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
5031         prefs->setDouble("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
5032         prefs->setDouble("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
5034         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5035     }
5037     gtk_toggle_action_set_active(act, false);
5040 static void
5041 sp_line_segment_build_list(GObject *tbl)
5043     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5045     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5046     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5047     gtk_list_store_clear (model);
5049     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5050     {
5051         GtkTreeIter iter;
5052         gtk_list_store_append( model, &iter );
5053         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5054         gtk_list_store_append( model, &iter );
5055         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5056         gtk_list_store_append( model, &iter );
5057         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5058         gtk_list_store_append( model, &iter );
5059         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5060     }
5062     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5065 static void
5066 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5067     using namespace Inkscape::LivePathEffect;
5069     // quit if run by the attr_changed listener
5070     if (g_object_get_data(tbl, "freeze")) {
5071         return;
5072     }
5074     // in turn, prevent listener from responding
5075     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5077     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5078     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5079     if (lpeitem) {
5080         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5081         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5082         sp_lpe_item_update_patheffect(lpeitem, true, true);
5083     }
5085     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5088 static void
5089 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5090     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5092     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5093         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5094     }
5095     gtk_toggle_action_set_active(act, false);
5098 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5100     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5101     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5102     g_object_set_data(holder, "tracker", tracker);
5103     SPUnit const *unit = tracker->getActiveUnit();
5105     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5106     prefs->setInt("tools.lpetool", "unitid", unit->unit_id);
5108     /** Automatically create a list of LPEs that get added to the toolbar **/
5109     {
5110         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5112         GtkTreeIter iter;
5114         // the first toggle button represents the state that no subtool is active (remove this when
5115         // this can be modeled by EgeSelectOneAction or some other action)
5116         gtk_list_store_append( model, &iter );
5117         gtk_list_store_set( model, &iter,
5118                             0, _("All inactive"),
5119                             1, _("No geometric tool is active"),
5120                             2, _("all_inactive"),
5121                             -1 );
5123         Inkscape::LivePathEffect::EffectType type;
5124         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5125             type =  lpesubtools[i];
5126             gtk_list_store_append( model, &iter );
5127             gtk_list_store_set( model, &iter,
5128                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5129                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5130                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5131                                 -1 );
5132         }
5134         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5135         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5136         g_object_set_data( holder, "lpetool_mode_action", act );
5138         ege_select_one_action_set_appearance( act, "full" );
5139         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5140         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5141         ege_select_one_action_set_icon_column( act, 2 );
5142         ege_select_one_action_set_tooltip_column( act, 1  );
5144         gint lpeToolMode = prefs->getInt("tools.lpetool", "mode", 0);
5145         ege_select_one_action_set_active( act, lpeToolMode );
5146         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5147     }
5149     /* Show limiting bounding box */
5150     {
5151         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5152                                                       _("Show limiting bounding box"),
5153                                                       _("Show bounding box (used to cut infinite lines)"),
5154                                                       "lpetool_show_bbox",
5155                                                       Inkscape::ICON_SIZE_DECORATION );
5156         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5157         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5158         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_bbox", 1 ) );
5159     }
5161     /* Set limiting bounding box to bbox of current selection */
5162     {
5163         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5164                                                       _("Get limiting bounding box from selection"),
5165                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5166                                                       "lpetool_set_bbox",
5167                                                       Inkscape::ICON_SIZE_DECORATION );
5168         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5169         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5170         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5171     }
5174     /* Combo box to choose line segment type */
5175     {
5176         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5177         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5178         ege_select_one_action_set_appearance (act, "compact");
5179         g_object_set_data (holder, "lpetool_line_segment_action", act );
5181         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5183         sp_line_segment_build_list (holder);
5185         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5186         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5187         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5188     }
5190     /* Display measuring info for selected items */
5191     {
5192         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5193                                                       _("Display measuring info"),
5194                                                       _("Display measuring info for selected items"),
5195                                                       "lpetool_measuring_info",
5196                                                       Inkscape::ICON_SIZE_DECORATION );
5197         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5198         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5199         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getInt( "tools.lpetool", "show_measuring_info", 1 ) );
5200     }
5202     // add the units menu
5203     {
5204         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5205         gtk_action_group_add_action( mainActions, act );
5206         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5207         g_object_set_data(holder, "lpetool_units_action", act);
5208         gtk_action_set_sensitive(act, prefs->getInt("tools.lpetool", "show_measuring_info", 1));
5209     }
5211     /* Open LPE dialog (to adapt parameters numerically) */
5212     {
5213         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5214                                                       _("Open LPE dialog"),
5215                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5216                                                       "lpetool_open_lpe_dialog",
5217                                                       Inkscape::ICON_SIZE_DECORATION );
5218         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5219         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5220         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5221     }
5223     //watch selection
5224     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5226     sigc::connection *c_selection_modified =
5227         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5228                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5229     pool->add_connection ("selection-modified", c_selection_modified);
5231     sigc::connection *c_selection_changed =
5232         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5233                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5234     pool->add_connection ("selection-changed", c_selection_changed);
5237 //########################
5238 //##       Eraser       ##
5239 //########################
5241 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5243     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5244     prefs->setDouble( "tools.eraser", "width", adj->value * 0.01 );
5245     update_presets_list(tbl);
5248 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5250     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5251     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5252     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5253         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5254         prefs->setInt( "tools.eraser", "mode", eraserMode );
5255     }
5257     // only take action if run by the attr_changed listener
5258     if (!g_object_get_data( tbl, "freeze" )) {
5259         // in turn, prevent listener from responding
5260         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5262         if ( eraserMode != 0 ) {
5263         } else {
5264         }
5265         // TODO finish implementation
5267         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5268     }
5271 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5273     {
5274         /* Width */
5275         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5276         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5277         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5278                                                               _("Pen Width"), _("Width:"),
5279                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5280                                                               "tools.eraser", "width", 15,
5281                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5282                                                               1, 100, 1.0, 0.0,
5283                                                               labels, values, G_N_ELEMENTS(labels),
5284                                                               sp_erc_width_value_changed,  0.01, 0, 100 );
5285         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5286         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5287         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5288     }
5290     {
5291         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5293         GtkTreeIter iter;
5294         gtk_list_store_append( model, &iter );
5295         gtk_list_store_set( model, &iter,
5296                             0, _("Delete"),
5297                             1, _("Delete objects touched by the eraser"),
5298                             2, "delete_object",
5299                             -1 );
5301         gtk_list_store_append( model, &iter );
5302         gtk_list_store_set( model, &iter,
5303                             0, _("Cut"),
5304                             1, _("Cut out from objects"),
5305                             2, "difference",
5306                             -1 );
5308         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5309         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5310         g_object_set_data( holder, "eraser_mode_action", act );
5312         ege_select_one_action_set_appearance( act, "full" );
5313         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5314         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5315         ege_select_one_action_set_icon_column( act, 2 );
5316         ege_select_one_action_set_tooltip_column( act, 1  );
5318         /// @todo Convert to boolean?
5319         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5320         gint eraserMode = (prefs->getInt("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5321         ege_select_one_action_set_active( act, eraserMode );
5322         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5323     }
5327 //########################
5328 //##    Text Toolbox    ##
5329 //########################
5330 /*
5331 static void
5332 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5334     //Call back for letter sizing spinbutton
5337 static void
5338 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5340     //Call back for line height spinbutton
5343 static void
5344 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5346     //Call back for horizontal kerning spinbutton
5349 static void
5350 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5352     //Call back for vertical kerning spinbutton
5355 static void
5356 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5358     //Call back for letter rotation spinbutton
5359 }*/
5361 namespace {
5363 bool popdown_visible = false;
5364 bool popdown_hasfocus = false;
5366 void
5367 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5369     SPStyle *query =
5370         sp_style_new (SP_ACTIVE_DOCUMENT);
5372 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5374     int result_family =
5375         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5377     int result_style =
5378         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5380     int result_numbers =
5381         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5383     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5385     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5386     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5387         // there are no texts in selection, read from prefs
5389         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5390         if (repr) {
5391             sp_style_read_from_repr (query, repr);
5392             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5393                 // do not reset the toolbar style from prefs if we already did it last time
5394                 sp_style_unref(query);
5395                 return;
5396             }
5397             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5398         } else {
5399             sp_style_unref(query);
5400             return;
5401         }
5402     } else {
5403         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5404     }
5406     if (query->text)
5407     {
5408         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5409             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5410             gtk_entry_set_text (GTK_ENTRY (entry), "");
5412         } else if (query->text->font_specification.value || query->text->font_family.value) {
5414             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5416             // Get the font that corresponds
5417             Glib::ustring familyName;
5419             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5420             if (font) {
5421                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5422                 font->Unref();
5423                 font = NULL;
5424             }
5426             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5428             Gtk::TreePath path;
5429             try {
5430                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5431             } catch (...) {
5432                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5433                 sp_style_unref(query);
5434                 return;
5435             }
5437             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5438             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5440             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5442             gtk_tree_selection_select_path (tselection, path.gobj());
5443             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5445             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5446         }
5448         //Size
5449         {
5450             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5451             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5452             g_object_set_data(tbl, "size-block", gpointer(1));
5453             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5454             g_object_set_data(tbl, "size-block", gpointer(0));
5455             g_free(str);
5456         }
5458         //Anchor
5459         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5460         {
5461             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5462             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5463             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5464             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5465         }
5466         else
5467         {
5468             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5469             {
5470                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5471                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5472                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5473                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5474             }
5475             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5476             {
5477                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5478                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5479                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5480                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5481             }
5482             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5483             {
5484                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5485                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5486                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5487                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5488             }
5489         }
5491         //Style
5492         {
5493             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5495             gboolean active = gtk_toggle_button_get_active (button);
5496             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5498             if (active != check)
5499             {
5500                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5501                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5502                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5503             }
5504         }
5506         {
5507             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5509             gboolean active = gtk_toggle_button_get_active (button);
5510             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5512             if (active != check)
5513             {
5514                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5515                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5516                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5517             }
5518         }
5520         //Orientation
5521         //locking both buttons, changing one affect all group (both)
5522         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5523         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5525         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5526         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5528         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5529         {
5530             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5531         }
5532         else
5533         {
5534             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5535         }
5536         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5537         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5538     }
5540     sp_style_unref(query);
5543 void
5544 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5546     sp_text_toolbox_selection_changed (selection, tbl);
5549 void
5550 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5552     sp_text_toolbox_selection_changed (NULL, tbl);
5555 void
5556 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5557                                 GObject             *tbl)
5559     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5560     GtkTreeModel *model = 0;
5561     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5562     GtkTreeIter   iter;
5563     char         *family = 0;
5565     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5566     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5568     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5569         return;
5570     }
5572     gtk_tree_model_get (model, &iter, 0, &family, -1);
5574     if (g_object_get_data (G_OBJECT (selection), "block"))
5575     {
5576         gtk_entry_set_text (GTK_ENTRY (entry), family);
5577         return;
5578     }
5580     gtk_entry_set_text (GTK_ENTRY (entry), family);
5582     SPStyle *query =
5583         sp_style_new (SP_ACTIVE_DOCUMENT);
5585     int result_fontspec =
5586         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5588     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5590     SPCSSAttr *css = sp_repr_css_attr_new ();
5593     // First try to get the font spec from the stored value
5594     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5596     if (fontSpec.empty()) {
5597         // Construct a new font specification if it does not yet exist
5598         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5599         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5600         fontFromStyle->Unref();
5601     }
5603     if (!fontSpec.empty()) {
5604         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5605         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5606             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5607             if (font) {
5608                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5610                 // Set all the these just in case they were altered when finding the best
5611                 // match for the new family and old style...
5613                 gchar c[256];
5615                 font->Family(c, 256);
5616                 sp_repr_css_set_property (css, "font-family", c);
5618                 font->Attribute( "weight", c, 256);
5619                 sp_repr_css_set_property (css, "font-weight", c);
5621                 font->Attribute("style", c, 256);
5622                 sp_repr_css_set_property (css, "font-style", c);
5624                 font->Attribute("stretch", c, 256);
5625                 sp_repr_css_set_property (css, "font-stretch", c);
5627                 font->Attribute("variant", c, 256);
5628                 sp_repr_css_set_property (css, "font-variant", c);
5630                 font->Unref();
5631             }
5632         }
5633     }
5635     // If querying returned nothing, set the default style of the tool (for new texts)
5636     if (result_fontspec == QUERY_STYLE_NOTHING)
5637     {
5638         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5639         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5640     }
5641     else
5642     {
5643         sp_desktop_set_style (desktop, css, true, true);
5644     }
5646     sp_style_unref(query);
5648     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5649                                    _("Text: Change font family"));
5650     sp_repr_css_attr_unref (css);
5651     g_free(family);
5652     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5654     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5657 /* This is where execution comes when the contents of the font family box have been completed
5658    by the press of the return key */
5659 void
5660 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5661                                        GObject      *tbl)
5663     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5665 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5666     try {
5667         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5668         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5669         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5670         gtk_tree_selection_select_path (selection, path.gobj());
5671         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5672         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5673     } catch (...) {
5674         if (family && strlen (family))
5675         {
5676             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5677         }
5678     }
5681 void
5682 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5683                                    gpointer          data)
5685     if (g_object_get_data (G_OBJECT (button), "block")) return;
5686     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5687     int prop = GPOINTER_TO_INT(data);
5689     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5690     SPCSSAttr *css = sp_repr_css_attr_new ();
5692     switch (prop)
5693     {
5694         case 0:
5695         {
5696             sp_repr_css_set_property (css, "text-anchor", "start");
5697             sp_repr_css_set_property (css, "text-align", "start");
5698             break;
5699         }
5700         case 1:
5701         {
5702             sp_repr_css_set_property (css, "text-anchor", "middle");
5703             sp_repr_css_set_property (css, "text-align", "center");
5704             break;
5705         }
5707         case 2:
5708         {
5709             sp_repr_css_set_property (css, "text-anchor", "end");
5710             sp_repr_css_set_property (css, "text-align", "end");
5711             break;
5712         }
5714         case 3:
5715         {
5716             sp_repr_css_set_property (css, "text-anchor", "start");
5717             sp_repr_css_set_property (css, "text-align", "justify");
5718             break;
5719         }
5720     }
5722     SPStyle *query =
5723         sp_style_new (SP_ACTIVE_DOCUMENT);
5724     int result_numbers =
5725         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5727     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5728     if (result_numbers == QUERY_STYLE_NOTHING)
5729     {
5730         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5731     }
5733     sp_style_unref(query);
5735     sp_desktop_set_style (desktop, css, true, true);
5736     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5737                                    _("Text: Change alignment"));
5738     sp_repr_css_attr_unref (css);
5740     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5743 void
5744 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5745                                gpointer          data)
5747     if (g_object_get_data (G_OBJECT (button), "block")) return;
5749     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5750     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5751     int          prop       = GPOINTER_TO_INT(data);
5752     bool         active     = gtk_toggle_button_get_active (button);
5754     SPStyle *query =
5755         sp_style_new (SP_ACTIVE_DOCUMENT);
5757     int result_fontspec =
5758         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5760     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5761     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5762     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5764     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5765     Glib::ustring newFontSpec = "";
5767     if (fontSpec.empty()) {
5768         // Construct a new font specification if it does not yet exist
5769         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5770         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5771         fontFromStyle->Unref();
5772     }
5774     switch (prop)
5775     {
5776         case 0:
5777         {
5778             if (!fontSpec.empty()) {
5779                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5780             }
5781             if (fontSpec != newFontSpec) {
5782                 // Don't even set the bold if the font didn't exist on the system
5783                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5784             }
5785             break;
5786         }
5788         case 1:
5789         {
5790             if (!fontSpec.empty()) {
5791                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5792             }
5793             if (fontSpec != newFontSpec) {
5794                 // Don't even set the italic if the font didn't exist on the system
5795                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5796             }
5797             break;
5798         }
5799     }
5801     if (!newFontSpec.empty()) {
5802         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5803     }
5805     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5806     if (result_fontspec == QUERY_STYLE_NOTHING)
5807     {
5808         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5809     }
5811     sp_style_unref(query);
5813     sp_desktop_set_style (desktop, css, true, true);
5814     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5815                                    _("Text: Change font style"));
5816     sp_repr_css_attr_unref (css);
5818     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5821 void
5822 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5823                                      gpointer         data)
5825     if (g_object_get_data (G_OBJECT (button), "block")) {
5826         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5827         return;
5828     }
5830     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5831     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5832     int          prop       = GPOINTER_TO_INT(data);
5834     switch (prop)
5835     {
5836         case 0:
5837         {
5838             sp_repr_css_set_property (css, "writing-mode", "lr");
5839             break;
5840         }
5842         case 1:
5843         {
5844             sp_repr_css_set_property (css, "writing-mode", "tb");
5845             break;
5846         }
5847     }
5849     SPStyle *query =
5850         sp_style_new (SP_ACTIVE_DOCUMENT);
5851     int result_numbers =
5852         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5854     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5855     if (result_numbers == QUERY_STYLE_NOTHING)
5856     {
5857         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5858     }
5860     sp_desktop_set_style (desktop, css, true, true);
5861     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5862                                    _("Text: Change orientation"));
5863     sp_repr_css_attr_unref (css);
5865     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5868 gboolean
5869 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5871     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5872     if (!desktop) return FALSE;
5874     switch (get_group0_keyval (event)) {
5875         case GDK_Escape: // defocus
5876             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5877             sp_text_toolbox_selection_changed (NULL, tbl); // update
5878             return TRUE; // I consumed the event
5879             break;
5880     }
5881     return FALSE;
5884 gboolean
5885 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5887     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5888     if (!desktop) return FALSE;
5890     switch (get_group0_keyval (event)) {
5891         case GDK_KP_Enter:
5892         case GDK_Return:
5893         case GDK_Escape: // defocus
5894             gtk_widget_hide (w);
5895             popdown_visible = false;
5896             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5897             return TRUE; // I consumed the event
5898             break;
5899         case GDK_w:
5900         case GDK_W:
5901             if (event->state & GDK_CONTROL_MASK) {
5902                 gtk_widget_hide (w);
5903                 popdown_visible = false;
5904                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5905                 return TRUE; // I consumed the event
5906             }
5907             break;
5908     }
5909     return FALSE;
5913 void
5914 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5915                                GObject     *tbl)
5917     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5919     if (g_object_get_data (tbl, "size-block")) return;
5921     // If this is not from selecting a size in the list (in which case get_active will give the
5922     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5923     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5924     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5925     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5926         return;
5928     gdouble value = -1;
5929     {
5930         gchar *endptr;
5931         gchar *const text = gtk_combo_box_get_active_text(cbox);
5932         if (text) {
5933             value = g_strtod(text, &endptr);
5934             if (endptr == text) {  // Conversion failed, non-numeric input.
5935                 value = -1;
5936             }
5937             g_free(text);
5938         }
5939     }
5940     if (value <= 0) {
5941         return; // could not parse value
5942     }
5944     SPCSSAttr *css = sp_repr_css_attr_new ();
5945     Inkscape::CSSOStringStream osfs;
5946     osfs << value;
5947     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5949     SPStyle *query =
5950         sp_style_new (SP_ACTIVE_DOCUMENT);
5951     int result_numbers =
5952         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5954     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5955     if (result_numbers == QUERY_STYLE_NOTHING)
5956     {
5957         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5958     }
5960     sp_style_unref(query);
5962     sp_desktop_set_style (desktop, css, true, true);
5963     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5964                                    _("Text: Change font size"));
5965     sp_repr_css_attr_unref (css);
5967     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5970 gboolean
5971 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5973     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5974     if (!desktop) return FALSE;
5976     if (!g_object_get_data (tbl, "esc-pressed")) {
5977         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5978         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5979         sp_text_toolbox_size_changed (cbox, tbl);
5980         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5981     }
5982     return FALSE; // I consumed the event
5986 gboolean
5987 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5989     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5990     if (!desktop) return FALSE;
5992     switch (get_group0_keyval (event)) {
5993         case GDK_Escape: // defocus
5994             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5995             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5996             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5997             return TRUE; // I consumed the event
5998             break;
5999         case GDK_Return: // defocus
6000         case GDK_KP_Enter:
6001             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6002             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6003             sp_text_toolbox_size_changed (cbox, tbl);
6004             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6005             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6006             return TRUE; // I consumed the event
6007             break;
6008     }
6009     return FALSE;
6012 void
6013 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
6014                                          GObject            *tbl)
6016     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6017     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6018     int x, y;
6020     if (!popdown_visible)
6021     {
6022         gdk_window_get_origin (widget->window, &x, &y);
6023         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6024         gtk_widget_show_all (popdown);
6025         //sp_transientize (popdown);
6027         gdk_pointer_grab (widget->window, TRUE,
6028                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6029                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6030                                         GDK_POINTER_MOTION_MASK),
6031                           NULL, NULL, GDK_CURRENT_TIME);
6033         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6035         popdown_visible = true;
6036     }
6037     else
6038     {
6039         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6040         gdk_pointer_ungrab (GDK_CURRENT_TIME);
6041         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6042         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6043         gtk_widget_hide (popdown);
6044         popdown_visible = false;
6045     }
6048 gboolean
6049 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6050                                  GdkEventFocus    */*event*/,
6051                                  GObject          */*tbl*/)
6053     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6054     return FALSE;
6057 gboolean
6058 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
6059                                    GdkEventFocus    */*event*/,
6060                                    GObject          */*tbl*/)
6062     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6064     if (popdown_hasfocus) {
6065         gtk_widget_hide (popdown);
6066         popdown_hasfocus = false;
6067         popdown_visible = false;
6068         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6069         return TRUE;
6070     }
6071     return FALSE;
6074 gboolean
6075 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
6076                                    GdkEventFocus    */*event*/,
6077                                    GObject          */*tbl*/)
6079     popdown_hasfocus = true;
6080     return TRUE;
6084 void
6085 cell_data_func  (GtkTreeViewColumn */*column*/,
6086                  GtkCellRenderer   *cell,
6087                  GtkTreeModel      *tree_model,
6088                  GtkTreeIter       *iter,
6089                  gpointer           /*data*/)
6091     gchar *family;
6092     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6093     gchar *const family_escaped = g_markup_escape_text(family, -1);
6095     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6096     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6098     std::stringstream markup;
6099     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6100            << family_escaped << "'>" << sample_escaped << "</span>";
6101     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6103     g_free(family);
6104     g_free(family_escaped);
6105     g_free(sample_escaped);
6108 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6109     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6110     if (completion) {
6111         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6112         g_object_unref (completion);
6113     }
6116 GtkWidget*
6117 sp_text_toolbox_new (SPDesktop *desktop)
6119     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6120     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
6122     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6123     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6125     GtkTooltips *tt = gtk_tooltips_new();
6126     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6128     ////////////Family
6129     //Window
6130     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6131     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6133     //Entry
6134     GtkWidget           *entry = gtk_entry_new ();
6135     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6136     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6137     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6138     gtk_entry_completion_set_text_column (completion, 0);
6139     gtk_entry_completion_set_minimum_key_length (completion, 1);
6140     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6141     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6142     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6143     gtk_toolbar_append_widget( tbl, entry, "", "" );
6144     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6146     //Button
6147     GtkWidget   *button = gtk_button_new ();
6148     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6149     gtk_toolbar_append_widget( tbl, button, "", "");
6151     //Popdown
6152     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6153     GtkWidget           *treeview = gtk_tree_view_new ();
6155     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6156     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6157     gtk_tree_view_column_pack_start (column, cell, FALSE);
6158     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6159     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6160     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6162     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6163     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6164     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6166     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6168     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6169     gtk_container_add (GTK_CONTAINER (sw), treeview);
6171     gtk_container_add (GTK_CONTAINER (window), sw);
6172     gtk_widget_set_size_request (window, 300, 450);
6174     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6175     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6176     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6178     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6180     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6181     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6182     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6184     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6185     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6187     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6188     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6189     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6190     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6191     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6193     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6194     GtkWidget *box = gtk_event_box_new ();
6195     gtk_container_add (GTK_CONTAINER (box), image);
6196     gtk_toolbar_append_widget( tbl, box, "", "");
6197     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6198     GtkTooltips *tooltips = gtk_tooltips_new ();
6199     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6200     gtk_widget_hide (GTK_WIDGET (box));
6201     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6203     ////////////Size
6204     gchar const *const sizes[] = {
6205         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6206         "16", "18", "20", "22", "24", "28",
6207         "32", "36", "40", "48", "56", "64", "72", "144"
6208     };
6210     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6211     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6212         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6213     }
6214     gtk_widget_set_size_request (cbox, 80, -1);
6215     gtk_toolbar_append_widget( tbl, cbox, "", "");
6216     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6217     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6218     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6219     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6221     ////////////Text anchor
6222     GtkWidget *group   = gtk_radio_button_new (NULL);
6223     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6224     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6226     // left
6227     GtkWidget *rbutton = group;
6228     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6229     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6230     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6232     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6233     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6234     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6235     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6237     // center
6238     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6239     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6240     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6241     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6243     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6244     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6245     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6246     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6248     // right
6249     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6250     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6251     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6252     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6254     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6255     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6256     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6257     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6259     // fill
6260     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6261     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6262     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6263     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6265     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6266     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6267     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6268     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6270     gtk_toolbar_append_widget( tbl, row, "", "");
6272     //spacer
6273     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6275     ////////////Text style
6276     row = gtk_hbox_new (FALSE, 4);
6278     // bold
6279     rbutton = gtk_toggle_button_new ();
6280     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6281     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6282     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6283     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6285     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6286     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6287     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6289     // italic
6290     rbutton = gtk_toggle_button_new ();
6291     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6292     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6293     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6294     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6296     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6297     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6298     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6300     gtk_toolbar_append_widget( tbl, row, "", "");
6302     //spacer
6303     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6305     ////////////Text orientation
6306     group   = gtk_radio_button_new (NULL);
6307     row     = gtk_hbox_new (FALSE, 4);
6308     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6310     // horizontal
6311     rbutton = group;
6312     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6313     gtk_container_add           (GTK_CONTAINER (rbutton),
6314                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6315     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6316     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6318     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6319     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6320     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6322     // vertical
6323     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6324     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6325     gtk_container_add           (GTK_CONTAINER (rbutton),
6326                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6327     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6328     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6330     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6331     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6332     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6333     gtk_toolbar_append_widget( tbl, row, "", "" );
6336     //watch selection
6337     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6339     sigc::connection *c_selection_changed =
6340         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6341                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6342     pool->add_connection ("selection-changed", c_selection_changed);
6344     sigc::connection *c_selection_modified =
6345         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6346                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6347     pool->add_connection ("selection-modified", c_selection_modified);
6349     sigc::connection *c_subselection_changed =
6350         new sigc::connection (desktop->connectToolSubselectionChanged
6351                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6352     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6354     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6357     gtk_widget_show_all( GTK_WIDGET(tbl) );
6359     return GTK_WIDGET(tbl);
6360 } // end of sp_text_toolbox_new()
6362 }//<unnamed> namespace
6365 //#########################
6366 //##      Connector      ##
6367 //#########################
6369 static void sp_connector_path_set_avoid(void)
6371     cc_selection_set_avoid(true);
6375 static void sp_connector_path_set_ignore(void)
6377     cc_selection_set_avoid(false);
6382 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6384     // quit if run by the _changed callbacks
6385     if (g_object_get_data( tbl, "freeze" )) {
6386         return;
6387     }
6389     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6390     SPDocument *doc = sp_desktop_document(desktop);
6392     if (!sp_document_get_undo_sensitive(doc))
6393     {
6394         return;
6395     }
6397     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6399     if ( repr->attribute("inkscape:connector-spacing") ) {
6400         gdouble priorValue = gtk_adjustment_get_value(adj);
6401         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6402         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6403             return;
6404         }
6405     } else if ( adj->value == defaultConnSpacing ) {
6406         return;
6407     }
6409     // in turn, prevent callbacks from responding
6410     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6412     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6413     SP_OBJECT(desktop->namedview)->updateRepr();
6415     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6416     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6417         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6418         Geom::Matrix m = Geom::identity();
6419         avoid_item_move(&m, item);
6420     }
6422     if (items) {
6423         g_slist_free(items);
6424     }
6426     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6427             _("Change connector spacing"));
6429     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6431     spinbutton_defocus(GTK_OBJECT(tbl));
6434 static void sp_connector_graph_layout(void)
6436     if (!SP_ACTIVE_DESKTOP) return;
6437     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6439     // hack for clones, see comment in align-and-distribute.cpp
6440     int saved_compensation = prefs->getInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6441     prefs->setInt("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6443     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6445     prefs->setInt("options.clonecompensation", "value", saved_compensation);
6447     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6450 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6452     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6453     prefs->setBool("tools.connector", "directedlayout",
6454                 gtk_toggle_action_get_active( act ));
6457 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6459     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6460     prefs->setBool("tools.connector", "avoidoverlaplayout",
6461                 gtk_toggle_action_get_active( act ));
6465 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6467     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6468     prefs->setDouble("tools.connector", "length", adj->value);
6469     spinbutton_defocus(GTK_OBJECT(tbl));
6472 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6473                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6474                                             bool /*is_interactive*/, gpointer data)
6476     GtkWidget *tbl = GTK_WIDGET(data);
6478     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6479         return;
6480     }
6481     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6482         return;
6483     }
6485     GtkAdjustment *adj = (GtkAdjustment*)
6486             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6487     gdouble spacing = defaultConnSpacing;
6488     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6490     gtk_adjustment_set_value(adj, spacing);
6494 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6495     NULL, /* child_added */
6496     NULL, /* child_removed */
6497     connector_tb_event_attr_changed,
6498     NULL, /* content_changed */
6499     NULL  /* order_changed */
6500 };
6503 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6505     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6506     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6508     {
6509         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6510                                           _("Avoid"),
6511                                           _("Make connectors avoid selected objects"),
6512                                           "connector_avoid",
6513                                           secondarySize );
6514         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6515         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6516     }
6518     {
6519         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6520                                           _("Ignore"),
6521                                           _("Make connectors ignore selected objects"),
6522                                           "connector_ignore",
6523                                           secondarySize );
6524         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6525         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6526     }
6528     EgeAdjustmentAction* eact = 0;
6530     // Spacing spinbox
6531     eact = create_adjustment_action( "ConnectorSpacingAction",
6532                                      _("Connector Spacing"), _("Spacing:"),
6533                                      _("The amount of space left around objects by auto-routing connectors"),
6534                                      "tools.connector", "spacing", defaultConnSpacing,
6535                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6536                                      0, 100, 1.0, 10.0,
6537                                      0, 0, 0,
6538                                      connector_spacing_changed, 1, 0 );
6539     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6541     // Graph (connector network) layout
6542     {
6543         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6544                                           _("Graph"),
6545                                           _("Nicely arrange selected connector network"),
6546                                           "graph_layout",
6547                                           secondarySize );
6548         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6549         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6550     }
6552     // Default connector length spinbox
6553     eact = create_adjustment_action( "ConnectorLengthAction",
6554                                      _("Connector Length"), _("Length:"),
6555                                      _("Ideal length for connectors when layout is applied"),
6556                                      "tools.connector", "length", 100,
6557                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6558                                      10, 1000, 10.0, 100.0,
6559                                      0, 0, 0,
6560                                      connector_length_changed, 1, 0 );
6561     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6564     // Directed edges toggle button
6565     {
6566         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6567                                                       _("Downwards"),
6568                                                       _("Make connectors with end-markers (arrows) point downwards"),
6569                                                       "directed_graph",
6570                                                       Inkscape::ICON_SIZE_DECORATION );
6571         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6573         bool tbuttonstate = prefs->getBool("tools.connector", "directedlayout");
6574         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
6576         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6577     }
6579     // Avoid overlaps toggle button
6580     {
6581         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6582                                                       _("Remove overlaps"),
6583                                                       _("Do not allow overlapping shapes"),
6584                                                       "remove_overlaps",
6585                                                       Inkscape::ICON_SIZE_DECORATION );
6586         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6588         bool tbuttonstate = prefs->getBool("tools.connector", "avoidoverlaplayout");
6589         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
6591         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6592     }
6594     // Code to watch for changes to the connector-spacing attribute in
6595     // the XML.
6596     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6597     g_assert(repr != NULL);
6599     purge_repr_listener( holder, holder );
6601     if (repr) {
6602         g_object_set_data( holder, "repr", repr );
6603         Inkscape::GC::anchor(repr);
6604         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6605         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6606     }
6607 } // end of sp_connector_toolbox_prep()
6610 //#########################
6611 //##     Paintbucket     ##
6612 //#########################
6614 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6616     gint channels = ege_select_one_action_get_active( act );
6617     flood_channels_set_channels( channels );
6620 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6622     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6623     prefs->setInt("tools.paintbucket", "threshold", (gint)adj->value);
6626 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6628     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6629     prefs->setInt("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6632 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6634     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6635     SPUnit const *unit = tracker->getActiveUnit();
6636     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6638     prefs->setDouble("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6639     prefs->setString("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6642 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6644     // FIXME: make defaults settable via Inkscape Options
6645     struct KeyValue {
6646         char const *key;
6647         double value;
6648     } const key_values[] = {
6649         {"threshold", 15},
6650         {"offset", 0.0}
6651     };
6653     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6654         KeyValue const &kv = key_values[i];
6655         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6656         if ( adj ) {
6657             gtk_adjustment_set_value(adj, kv.value);
6658         }
6659     }
6661     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6662     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6663     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6664     ege_select_one_action_set_active( autogap_action, 0 );
6667 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6669     EgeAdjustmentAction* eact = 0;
6670     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6672     {
6673         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6675         GList* items = 0;
6676         gint count = 0;
6677         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6678         {
6679             GtkTreeIter iter;
6680             gtk_list_store_append( model, &iter );
6681             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6682             count++;
6683         }
6684         g_list_free( items );
6685         items = 0;
6686         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6687         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6688         ege_select_one_action_set_appearance( act1, "compact" );
6689         ege_select_one_action_set_active( act1, prefs->getInt("tools.paintbucket", "channels", 0) );
6690         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6691         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6692         g_object_set_data( holder, "channels_action", act1 );
6693     }
6695     // Spacing spinbox
6696     {
6697         eact = create_adjustment_action(
6698             "ThresholdAction",
6699             _("Fill Threshold"), _("Threshold:"),
6700             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6701             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6702             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6703             0, 0, 0,
6704             paintbucket_threshold_changed, 1, 0 );
6706         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6707         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6708     }
6710     // Create the units menu.
6711     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6712     Glib::ustring stored_unit = prefs->getString("tools.paintbucket", "offsetunits");
6713     if (!stored_unit.empty())
6714         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
6715     g_object_set_data( holder, "tracker", tracker );
6716     {
6717         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6718         gtk_action_group_add_action( mainActions, act );
6719     }
6721     // Offset spinbox
6722     {
6723         eact = create_adjustment_action(
6724             "OffsetAction",
6725             _("Grow/shrink by"), _("Grow/shrink by:"),
6726             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6727             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6728             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6729             0, 0, 0,
6730             paintbucket_offset_changed, 1, 2);
6731         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6733         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6734     }
6736     /* Auto Gap */
6737     {
6738         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6740         GList* items = 0;
6741         gint count = 0;
6742         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6743         {
6744             GtkTreeIter iter;
6745             gtk_list_store_append( model, &iter );
6746             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6747             count++;
6748         }
6749         g_list_free( items );
6750         items = 0;
6751         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6752         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6753         ege_select_one_action_set_appearance( act2, "compact" );
6754         ege_select_one_action_set_active( act2, prefs->getInt("tools.paintbucket", "autogap", 0) );
6755         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6756         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6757         g_object_set_data( holder, "autogap_action", act2 );
6758     }
6760     /* Reset */
6761     {
6762         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6763                                           _("Defaults"),
6764                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6765                                           GTK_STOCK_CLEAR );
6766         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6767         gtk_action_group_add_action( mainActions, act );
6768         gtk_action_set_sensitive( act, TRUE );
6769     }
6773 /*
6774   Local Variables:
6775   mode:c++
6776   c-file-style:"stroustrup"
6777   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6778   indent-tabs-mode:nil
6779   fill-column:99
6780   End:
6781 */
6782 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :