Code

ab2153fe0386722c343f0cf982bbaec7ff9876b5
[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 "prefs-utils.h"
49 #include "verbs.h"
50 #include "sp-namedview.h"
51 #include "desktop.h"
52 #include "desktop-handles.h"
53 #include "xml/repr.h"
54 #include "xml/node-event-vector.h"
55 #include "xml/attribute-record.h"
56 #include <glibmm/i18n.h>
57 #include "helper/unit-menu.h"
58 #include "helper/units.h"
59 #include "live_effects/effect.h"
61 #include "inkscape.h"
62 #include "conn-avoid-ref.h"
65 #include "select-toolbar.h"
66 #include "gradient-toolbar.h"
68 #include "connector-context.h"
69 #include "node-context.h"
70 #include "pen-context.h"
71 #include "lpe-tool-context.h"
72 #include "live_effects/lpe-line_segment.h"
73 #include "shape-editor.h"
74 #include "tweak-context.h"
75 #include "sp-rect.h"
76 #include "box3d.h"
77 #include "box3d-context.h"
78 #include "sp-star.h"
79 #include "sp-spiral.h"
80 #include "sp-ellipse.h"
81 #include "sp-text.h"
82 #include "sp-flowtext.h"
83 #include "sp-clippath.h"
84 #include "sp-mask.h"
85 #include "style.h"
86 #include "tools-switch.h"
87 #include "selection.h"
88 #include "selection-chemistry.h"
89 #include "document-private.h"
90 #include "desktop-style.h"
91 #include "../libnrtype/font-lister.h"
92 #include "../libnrtype/font-instance.h"
93 #include "../connection-pool.h"
94 #include "../prefs-utils.h"
95 #include "../inkscape-stock.h"
96 #include "icon.h"
97 #include "graphlayout/graphlayout.h"
98 #include "interface.h"
99 #include "shortcuts.h"
101 #include "mod360.h"
103 #include "toolbox.h"
105 #include "flood-context.h"
107 #include "ink-action.h"
108 #include "ege-adjustment-action.h"
109 #include "ege-output-action.h"
110 #include "ege-select-one-action.h"
111 #include "helper/unit-tracker.h"
112 #include "live_effects/lpe-angle_bisector.h"
114 #include "svg/css-ostringstream.h"
116 #include "widgets/calligraphic-profile-rename.h"
118 using Inkscape::UnitTracker;
120 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
121 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
123 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
136 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
139 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
144 Inkscape::IconSize prefToSize( gchar const *path, gchar const *attr, int base ) {
145     static Inkscape::IconSize sizeChoices[] = {
146         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
147         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
148         Inkscape::ICON_SIZE_MENU
149     };
150     int index = prefs_get_int_attribute_limited( 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     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs_get_double_attribute(path, data, def) * factor,
927                                                              lower, upper, step, page, page ) );
928     if (us) {
929         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
930     }
932     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
934     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
935     if ( shortLabel ) {
936         g_object_set( act, "short_label", shortLabel, NULL );
937     }
939     if ( (descrCount > 0) && descrLabels && descrValues ) {
940         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
941     }
943     if ( focusTarget ) {
944         ege_adjustment_action_set_focuswidget( act, focusTarget );
945     }
947     if ( altx && altx_mark ) {
948         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
949     }
951     if ( dataKludge ) {
952         g_object_set_data( dataKludge, data, adj );
953     }
955     // Using a cast just to make sure we pass in the right kind of function pointer
956     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
958     return act;
962 //####################################
963 //# node editing callbacks
964 //####################################
966 /**
967  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
968  */
969 static ShapeEditor *get_current_shape_editor()
971     if (!SP_ACTIVE_DESKTOP) {
972         return NULL;
973     }
975     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
977     if (!SP_IS_NODE_CONTEXT(event_context)) {
978         return NULL;
979     }
981     return SP_NODE_CONTEXT(event_context)->shape_editor;
985 void
986 sp_node_path_edit_add(void)
988     ShapeEditor *shape_editor = get_current_shape_editor();
989     if (shape_editor) shape_editor->add_node();
992 void
993 sp_node_path_edit_delete(void)
995     ShapeEditor *shape_editor = get_current_shape_editor();
996     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
999 void
1000 sp_node_path_edit_delete_segment(void)
1002     ShapeEditor *shape_editor = get_current_shape_editor();
1003     if (shape_editor) shape_editor->delete_segment();
1006 void
1007 sp_node_path_edit_break(void)
1009     ShapeEditor *shape_editor = get_current_shape_editor();
1010     if (shape_editor) shape_editor->break_at_nodes();
1013 void
1014 sp_node_path_edit_join(void)
1016     ShapeEditor *shape_editor = get_current_shape_editor();
1017     if (shape_editor) shape_editor->join_nodes();
1020 void
1021 sp_node_path_edit_join_segment(void)
1023     ShapeEditor *shape_editor = get_current_shape_editor();
1024     if (shape_editor) shape_editor->join_segments();
1027 void
1028 sp_node_path_edit_toline(void)
1030     ShapeEditor *shape_editor = get_current_shape_editor();
1031     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1034 void
1035 sp_node_path_edit_tocurve(void)
1037     ShapeEditor *shape_editor = get_current_shape_editor();
1038     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1041 void
1042 sp_node_path_edit_cusp(void)
1044     ShapeEditor *shape_editor = get_current_shape_editor();
1045     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1048 void
1049 sp_node_path_edit_smooth(void)
1051     ShapeEditor *shape_editor = get_current_shape_editor();
1052     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1055 void
1056 sp_node_path_edit_symmetrical(void)
1058     ShapeEditor *shape_editor = get_current_shape_editor();
1059     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1062 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1063     bool show = gtk_toggle_action_get_active( act );
1064     prefs_set_int_attribute ("tools.nodes", "show_handles",  show ? 1 : 0);
1065     ShapeEditor *shape_editor = get_current_shape_editor();
1066     if (shape_editor) shape_editor->show_handles(show);
1069 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1070     bool show = gtk_toggle_action_get_active( act );
1071     prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
1072     ShapeEditor *shape_editor = get_current_shape_editor();
1073     if (shape_editor) shape_editor->show_helperpath(show);
1076 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1077     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1080 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1081     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1084 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1085     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1088 /* is called when the node selection is modified */
1089 static void
1090 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1092     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1093     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1094     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1095     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1097     // quit if run by the attr_changed listener
1098     if (g_object_get_data( tbl, "freeze" )) {
1099         return;
1100     }
1102     // in turn, prevent listener from responding
1103     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1105     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1106     SPUnit const *unit = tracker->getActiveUnit();
1108     ShapeEditor *shape_editor = get_current_shape_editor();
1109     if (shape_editor && shape_editor->has_nodepath()) {
1110         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1111         int n_selected = 0;
1112         if (nodepath) {
1113             n_selected = nodepath->numSelected();
1114         }
1116         if (n_selected == 0) {
1117             gtk_action_set_sensitive(xact, FALSE);
1118             gtk_action_set_sensitive(yact, FALSE);
1119         } else {
1120             gtk_action_set_sensitive(xact, TRUE);
1121             gtk_action_set_sensitive(yact, TRUE);
1122             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1123             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1125             if (n_selected == 1) {
1126                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1127                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1128                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1129                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1130                 }
1131             } else {
1132                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1133                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1134                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1135                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1136                        selected nodes don't coincide (in this case we use the coordinates of the center
1137                        of the bounding box). So the entries are never set to zero. */
1138                     // FIXME: Maybe we should clear the entry if several nodes are selected
1139                     //        instead of providing a kind of average value
1140                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1141                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1142                 }
1143             }
1144         }
1145     } else {
1146         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1147         gtk_action_set_sensitive(xact, FALSE);
1148         gtk_action_set_sensitive(yact, FALSE);
1149     }
1151     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1154 static void
1155 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1157     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1159     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1160     SPUnit const *unit = tracker->getActiveUnit();
1162     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1163         prefs_set_double_attribute("tools.nodes", value_name, sp_units_get_pixels(adj->value, *unit));
1164     }
1166     // quit if run by the attr_changed listener
1167     if (g_object_get_data( tbl, "freeze" )) {
1168         return;
1169     }
1171     // in turn, prevent listener from responding
1172     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1174     ShapeEditor *shape_editor = get_current_shape_editor();
1175     if (shape_editor && shape_editor->has_nodepath()) {
1176         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1177         if (!strcmp(value_name, "x")) {
1178             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1179         }
1180         if (!strcmp(value_name, "y")) {
1181             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1182         }
1183     }
1185     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1188 static void
1189 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1191     sp_node_path_value_changed(adj, tbl, "x");
1194 static void
1195 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1197     sp_node_path_value_changed(adj, tbl, "y");
1200 void
1201 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1203     {
1204     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1205     SPItem *item = selection->singleItem();
1206     if (item && SP_IS_LPE_ITEM(item)) {
1207        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1208            gtk_action_set_sensitive(w, TRUE);
1209        } else {
1210            gtk_action_set_sensitive(w, FALSE);
1211        }
1212     } else {
1213        gtk_action_set_sensitive(w, FALSE);
1214     }
1215     }
1217     {
1218     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1219     SPItem *item = selection->singleItem();
1220     if (item && item->clip_ref && item->clip_ref->getObject()) {
1221        gtk_action_set_sensitive(w, TRUE);
1222     } else {
1223        gtk_action_set_sensitive(w, FALSE);
1224     }
1225     }
1227     {
1228     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1229     SPItem *item = selection->singleItem();
1230     if (item && item->mask_ref && item->mask_ref->getObject()) {
1231        gtk_action_set_sensitive(w, TRUE);
1232     } else {
1233        gtk_action_set_sensitive(w, FALSE);
1234     }
1235     }
1238 void
1239 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1241     sp_node_toolbox_sel_changed (selection, tbl);
1246 //################################
1247 //##    Node Editing Toolbox    ##
1248 //################################
1250 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1252     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1253     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1254     g_object_set_data( holder, "tracker", tracker );
1256     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
1258     {
1259         InkAction* inky = ink_action_new( "NodeInsertAction",
1260                                           _("Insert node"),
1261                                           _("Insert new nodes into selected segments"),
1262                                           "node_insert",
1263                                           secondarySize );
1264         g_object_set( inky, "short_label", _("Insert"), NULL );
1265         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1266         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1267     }
1269     {
1270         InkAction* inky = ink_action_new( "NodeDeleteAction",
1271                                           _("Delete node"),
1272                                           _("Delete selected nodes"),
1273                                           "node_delete",
1274                                           secondarySize );
1275         g_object_set( inky, "short_label", _("Delete"), NULL );
1276         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1277         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1278     }
1280     {
1281         InkAction* inky = ink_action_new( "NodeJoinAction",
1282                                           _("Join endnodes"),
1283                                           _("Join selected endnodes"),
1284                                           "node_join",
1285                                           secondarySize );
1286         g_object_set( inky, "short_label", _("Join"), NULL );
1287         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1288         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1289     }
1291     {
1292         InkAction* inky = ink_action_new( "NodeBreakAction",
1293                                           _("Break nodes"),
1294                                           _("Break path at selected nodes"),
1295                                           "node_break",
1296                                           secondarySize );
1297         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1298         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1299     }
1302     {
1303         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1304                                           _("Join with segment"),
1305                                           _("Join selected endnodes with a new segment"),
1306                                           "node_join_segment",
1307                                           secondarySize );
1308         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1309         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1310     }
1312     {
1313         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1314                                           _("Delete segment"),
1315                                           _("Delete segment between two non-endpoint nodes"),
1316                                           "node_delete_segment",
1317                                           secondarySize );
1318         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1319         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1320     }
1322     {
1323         InkAction* inky = ink_action_new( "NodeCuspAction",
1324                                           _("Node Cusp"),
1325                                           _("Make selected nodes corner"),
1326                                           "node_cusp",
1327                                           secondarySize );
1328         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1329         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1330     }
1332     {
1333         InkAction* inky = ink_action_new( "NodeSmoothAction",
1334                                           _("Node Smooth"),
1335                                           _("Make selected nodes smooth"),
1336                                           "node_smooth",
1337                                           secondarySize );
1338         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1339         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1340     }
1342     {
1343         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1344                                           _("Node Symmetric"),
1345                                           _("Make selected nodes symmetric"),
1346                                           "node_symmetric",
1347                                           secondarySize );
1348         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1349         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1350     }
1352     {
1353         InkAction* inky = ink_action_new( "NodeLineAction",
1354                                           _("Node Line"),
1355                                           _("Make selected segments lines"),
1356                                           "node_line",
1357                                           secondarySize );
1358         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1359         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1360     }
1362     {
1363         InkAction* inky = ink_action_new( "NodeCurveAction",
1364                                           _("Node Curve"),
1365                                           _("Make selected segments curves"),
1366                                           "node_curve",
1367                                           secondarySize );
1368         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1369         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1370     }
1372     {
1373         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1374                                                       _("Show Handles"),
1375                                                       _("Show the Bezier handles of selected nodes"),
1376                                                       "nodes_show_handles",
1377                                                       Inkscape::ICON_SIZE_DECORATION );
1378         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1379         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1380         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
1381     }
1383     {
1384         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1385                                                       _("Show Outline"),
1386                                                       _("Show the outline of the path"),
1387                                                       "nodes_show_helperpath",
1388                                                       Inkscape::ICON_SIZE_DECORATION );
1389         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1390         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1391         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
1392     }
1394     {
1395         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1396                                           _("Next path effect parameter"),
1397                                           _("Show next path effect parameter for editing"),
1398                                           "edit_next_parameter",
1399                                           Inkscape::ICON_SIZE_DECORATION );
1400         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1401         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402         g_object_set_data( holder, "nodes_lpeedit", inky);
1403     }
1405     {
1406         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1407                                           _("Edit clipping path"),
1408                                           _("Edit the clipping path of the object"),
1409                                           "nodeedit-clippath",
1410                                           Inkscape::ICON_SIZE_DECORATION );
1411         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1412         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1413         g_object_set_data( holder, "nodes_clippathedit", inky);
1414     }
1416     {
1417         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1418                                           _("Edit mask path"),
1419                                           _("Edit the mask of the object"),
1420                                           "nodeedit-mask",
1421                                           Inkscape::ICON_SIZE_DECORATION );
1422         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1423         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1424         g_object_set_data( holder, "nodes_maskedit", inky);
1425     }
1427     /* X coord of selected node(s) */
1428     {
1429         EgeAdjustmentAction* eact = 0;
1430         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1431         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1432         eact = create_adjustment_action( "NodeXAction",
1433                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1434                                          "tools.nodes", "Xcoord", 0,
1435                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1436                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1437                                          labels, values, G_N_ELEMENTS(labels),
1438                                          sp_node_path_x_value_changed );
1439         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1440         g_object_set_data( holder, "nodes_x_action", eact );
1441         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1442         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1443     }
1445     /* Y coord of selected node(s) */
1446     {
1447         EgeAdjustmentAction* eact = 0;
1448         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1449         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1450         eact = create_adjustment_action( "NodeYAction",
1451                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1452                                          "tools.nodes", "Ycoord", 0,
1453                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1454                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1455                                          labels, values, G_N_ELEMENTS(labels),
1456                                          sp_node_path_y_value_changed );
1457         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1458         g_object_set_data( holder, "nodes_y_action", eact );
1459         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1460         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1461     }
1463     // add the units menu
1464     {
1465         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1466         gtk_action_group_add_action( mainActions, act );
1467     }
1470     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1472     //watch selection
1473     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1475     sigc::connection *c_selection_changed =
1476         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1477                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1478     pool->add_connection ("selection-changed", c_selection_changed);
1480     sigc::connection *c_selection_modified =
1481         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1482                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1483     pool->add_connection ("selection-modified", c_selection_modified);
1485     sigc::connection *c_subselection_changed =
1486         new sigc::connection (desktop->connectToolSubselectionChanged
1487                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1488     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1490     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1492     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1493 } // end of sp_node_toolbox_prep()
1496 //########################
1497 //##    Zoom Toolbox    ##
1498 //########################
1500 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1502     // no custom GtkAction setup needed
1503 } // end of sp_zoom_toolbox_prep()
1505 void
1506 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1508     toolbox_set_desktop(toolbox,
1509                         desktop,
1510                         setup_tool_toolbox,
1511                         update_tool_toolbox,
1512                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1513                                                                          "event_context_connection")));
1517 void
1518 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1520     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1521                         desktop,
1522                         setup_aux_toolbox,
1523                         update_aux_toolbox,
1524                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1525                                                                          "event_context_connection")));
1528 void
1529 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1531     toolbox_set_desktop(toolbox,
1532                         desktop,
1533                         setup_commands_toolbox,
1534                         update_commands_toolbox,
1535                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1536                                                                          "event_context_connection")));
1539 static void
1540 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1542     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1543     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1545     if (old_desktop) {
1546         GList *children, *iter;
1548         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1549         for ( iter = children ; iter ; iter = iter->next ) {
1550             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1551         }
1552         g_list_free(children);
1553     }
1555     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1557     if (desktop) {
1558         gtk_widget_set_sensitive(toolbox, TRUE);
1559         setup_func(toolbox, desktop);
1560         update_func(desktop, desktop->event_context, toolbox);
1561         *conn = desktop->connectEventContextChanged
1562             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1563     } else {
1564         gtk_widget_set_sensitive(toolbox, FALSE);
1565     }
1567 } // end of toolbox_set_desktop()
1570 static void
1571 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1573     gchar const * descr =
1574         "<ui>"
1575         "  <toolbar name='ToolToolbar'>"
1576         "    <toolitem action='ToolSelector' />"
1577         "    <toolitem action='ToolNode' />"
1578         "    <toolitem action='ToolTweak' />"
1579         "    <toolitem action='ToolZoom' />"
1580         "    <toolitem action='ToolRect' />"
1581         "    <toolitem action='Tool3DBox' />"
1582         "    <toolitem action='ToolArc' />"
1583         "    <toolitem action='ToolStar' />"
1584         "    <toolitem action='ToolSpiral' />"
1585         "    <toolitem action='ToolPencil' />"
1586         "    <toolitem action='ToolPen' />"
1587         "    <toolitem action='ToolCalligraphic' />"
1588         "    <toolitem action='ToolEraser' />"
1589 //        "    <toolitem action='ToolLPETool' />"
1590         "    <toolitem action='ToolPaintBucket' />"
1591         "    <toolitem action='ToolText' />"
1592         "    <toolitem action='ToolConnector' />"
1593         "    <toolitem action='ToolGradient' />"
1594         "    <toolitem action='ToolDropper' />"
1595         "  </toolbar>"
1596         "</ui>";
1597     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1598     GtkUIManager* mgr = gtk_ui_manager_new();
1599     GError* errVal = 0;
1601     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1602     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1604     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1605     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1606         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1607     }
1608     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1609     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1611     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1612     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1614     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1616     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1617     if ( child ) {
1618         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1619     }
1621     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1622 //     Inkscape::IconSize toolboxSize = prefToSize("toolbox.tools", "small");
1626 static void
1627 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1629     gchar const *const tname = ( eventcontext
1630                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1631                                  : NULL );
1632     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1634     for (int i = 0 ; tools[i].type_name ; i++ ) {
1635         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1636         if ( act ) {
1637             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1638             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1639             if ( verbAct ) {
1640                 verbAct->set_active(setActive);
1641             }
1642         }
1643     }
1646 static void
1647 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1649     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1650     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1651     GtkUIManager* mgr = gtk_ui_manager_new();
1652     GError* errVal = 0;
1653     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1654     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1656     std::map<std::string, GtkWidget*> dataHolders;
1658     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1659         if ( aux_toolboxes[i].prep_func ) {
1660             // converted to GtkActions and UIManager
1662             GtkWidget* kludge = gtk_toolbar_new();
1663             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1664             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1665             dataHolders[aux_toolboxes[i].type_name] = kludge;
1666             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1667         } else {
1669             GtkWidget *sub_toolbox = 0;
1670             if (aux_toolboxes[i].create_func == NULL)
1671                 sub_toolbox = sp_empty_toolbox_new(desktop);
1672             else {
1673                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1674             }
1676             gtk_size_group_add_widget( grouper, sub_toolbox );
1678             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1679             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1681         }
1682     }
1684     // Second pass to create toolbars *after* all GtkActions are created
1685     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1686         if ( aux_toolboxes[i].prep_func ) {
1687             // converted to GtkActions and UIManager
1689             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1691             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1692             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1694             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1695             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1696             g_free( tmp );
1697             tmp = 0;
1699             Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1700             if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1701                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1702             }
1703             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1706             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1708             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1709                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1710                 swatch->setDesktop( desktop );
1711                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1712                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1713                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1714                 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 );
1715             }
1717             gtk_widget_show_all( holder );
1718             sp_set_font_size_smaller( holder );
1720             gtk_size_group_add_widget( grouper, holder );
1722             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1723             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1724         }
1725     }
1727     g_object_unref( G_OBJECT(grouper) );
1730 static void
1731 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1733     gchar const *tname = ( eventcontext
1734                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1735                            : NULL );
1736     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1737         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1738         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1739             gtk_widget_show_all(sub_toolbox);
1740             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1741         } else {
1742             gtk_widget_hide(sub_toolbox);
1743         }
1744     }
1747 static void
1748 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1750     gchar const * descr =
1751         "<ui>"
1752         "  <toolbar name='CommandsToolbar'>"
1753         "    <toolitem action='FileNew' />"
1754         "    <toolitem action='FileOpen' />"
1755         "    <toolitem action='FileSave' />"
1756         "    <toolitem action='FilePrint' />"
1757         "    <separator />"
1758         "    <toolitem action='FileImport' />"
1759         "    <toolitem action='FileExport' />"
1760         "    <separator />"
1761         "    <toolitem action='EditUndo' />"
1762         "    <toolitem action='EditRedo' />"
1763         "    <separator />"
1764         "    <toolitem action='EditCopy' />"
1765         "    <toolitem action='EditCut' />"
1766         "    <toolitem action='EditPaste' />"
1767         "    <separator />"
1768         "    <toolitem action='ZoomSelection' />"
1769         "    <toolitem action='ZoomDrawing' />"
1770         "    <toolitem action='ZoomPage' />"
1771         "    <separator />"
1772         "    <toolitem action='EditDuplicate' />"
1773         "    <toolitem action='EditClone' />"
1774         "    <toolitem action='EditUnlinkClone' />"
1775         "    <separator />"
1776         "    <toolitem action='SelectionGroup' />"
1777         "    <toolitem action='SelectionUnGroup' />"
1778         "    <separator />"
1779         "    <toolitem action='DialogFillStroke' />"
1780         "    <toolitem action='DialogText' />"
1781         "    <toolitem action='DialogXMLEditor' />"
1782         "    <toolitem action='DialogAlignDistribute' />"
1783         "    <separator />"
1784         "    <toolitem action='DialogPreferences' />"
1785         "    <toolitem action='DialogDocumentProperties' />"
1786         "  </toolbar>"
1787         "</ui>";
1788     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1791     GtkUIManager* mgr = gtk_ui_manager_new();
1792     GError* errVal = 0;
1794     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1795     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1797     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1798     if ( prefs_get_int_attribute_limited( "toolbox", "icononly", 1, 0, 1 ) ) {
1799         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1800     }
1802     Inkscape::IconSize toolboxSize = prefToSize("toolbox", "small");
1803     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1805     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1806     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1809     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1811     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1812     if ( child ) {
1813         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1814     }
1816     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1819 static void
1820 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1824 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
1826     gtk_widget_show(toolbox_toplevel);
1827     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
1829     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
1830     if (!shown_toolbox) {
1831         return;
1832     }
1833     gtk_widget_show(toolbox);
1835     gtk_widget_show_all(shown_toolbox);
1838 static GtkWidget *
1839 sp_empty_toolbox_new(SPDesktop *desktop)
1841     GtkWidget *tbl = gtk_toolbar_new();
1842     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
1843     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
1845     gtk_widget_show_all(tbl);
1846     sp_set_font_size_smaller (tbl);
1848     return tbl;
1851 #define MODE_LABEL_WIDTH 70
1853 //########################
1854 //##       Star         ##
1855 //########################
1857 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1859     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1861     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1862         // do not remember prefs if this call is initiated by an undo change, because undoing object
1863         // creation sets bogus values to its attributes before it is deleted
1864         prefs_set_int_attribute("tools.shapes.star", "magnitude", (gint)adj->value);
1865     }
1867     // quit if run by the attr_changed listener
1868     if (g_object_get_data( dataKludge, "freeze" )) {
1869         return;
1870     }
1872     // in turn, prevent listener from responding
1873     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1875     bool modmade = false;
1877     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1878     GSList const *items = selection->itemList();
1879     for (; items != NULL; items = items->next) {
1880         if (SP_IS_STAR((SPItem *) items->data)) {
1881             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1882             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
1883             sp_repr_set_svg_double(repr, "sodipodi:arg2",
1884                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
1885                                     + M_PI / (gint)adj->value));
1886             SP_OBJECT((SPItem *) items->data)->updateRepr();
1887             modmade = true;
1888         }
1889     }
1890     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1891                                    _("Star: Change number of corners"));
1893     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1896 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1898     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1900     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1901         prefs_set_double_attribute("tools.shapes.star", "proportion", adj->value);
1902     }
1904     // quit if run by the attr_changed listener
1905     if (g_object_get_data( dataKludge, "freeze" )) {
1906         return;
1907     }
1909     // in turn, prevent listener from responding
1910     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1912     bool modmade = false;
1913     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1914     GSList const *items = selection->itemList();
1915     for (; items != NULL; items = items->next) {
1916         if (SP_IS_STAR((SPItem *) items->data)) {
1917             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1919             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
1920             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
1921             if (r2 < r1) {
1922                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
1923             } else {
1924                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
1925             }
1927             SP_OBJECT((SPItem *) items->data)->updateRepr();
1928             modmade = true;
1929         }
1930     }
1932     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1933                                    _("Star: Change spoke ratio"));
1935     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1938 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
1940     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1941     bool flat = ege_select_one_action_get_active( act ) == 0;
1943     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1944         prefs_set_string_attribute( "tools.shapes.star", "isflatsided",
1945                                     flat ? "true" : "false" );
1946     }
1948     // quit if run by the attr_changed listener
1949     if (g_object_get_data( dataKludge, "freeze" )) {
1950         return;
1951     }
1953     // in turn, prevent listener from responding
1954     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1956     Inkscape::Selection *selection = sp_desktop_selection(desktop);
1957     GSList const *items = selection->itemList();
1958     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
1959     bool modmade = false;
1961     if ( prop_action ) {
1962         gtk_action_set_sensitive( prop_action, !flat );
1963     }
1965     for (; items != NULL; items = items->next) {
1966         if (SP_IS_STAR((SPItem *) items->data)) {
1967             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
1968             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
1969             SP_OBJECT((SPItem *) items->data)->updateRepr();
1970             modmade = true;
1971         }
1972     }
1974     if (modmade) {
1975         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
1976                          flat ? _("Make polygon") : _("Make star"));
1977     }
1979     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
1982 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
1984     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
1986     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1987         prefs_set_double_attribute("tools.shapes.star", "rounded", (gdouble) adj->value);
1988     }
1990     // quit if run by the attr_changed listener
1991     if (g_object_get_data( dataKludge, "freeze" )) {
1992         return;
1993     }
1995     // in turn, prevent listener from responding
1996     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
1998     bool modmade = false;
2000     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2001     GSList const *items = selection->itemList();
2002     for (; items != NULL; items = items->next) {
2003         if (SP_IS_STAR((SPItem *) items->data)) {
2004             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2005             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2006             SP_OBJECT(items->data)->updateRepr();
2007             modmade = true;
2008         }
2009     }
2010     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2011                                    _("Star: Change rounding"));
2013     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2016 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2018     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2020     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2021         prefs_set_double_attribute("tools.shapes.star", "randomized", (gdouble) adj->value);
2022     }
2024     // quit if run by the attr_changed listener
2025     if (g_object_get_data( dataKludge, "freeze" )) {
2026         return;
2027     }
2029     // in turn, prevent listener from responding
2030     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2032     bool modmade = false;
2034     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2035     GSList const *items = selection->itemList();
2036     for (; items != NULL; items = items->next) {
2037         if (SP_IS_STAR((SPItem *) items->data)) {
2038             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2039             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2040             SP_OBJECT(items->data)->updateRepr();
2041             modmade = true;
2042         }
2043     }
2044     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2045                                    _("Star: Change randomization"));
2047     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2051 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2052                                        gchar const */*old_value*/, gchar const */*new_value*/,
2053                                        bool /*is_interactive*/, gpointer data)
2055     GtkWidget *tbl = GTK_WIDGET(data);
2057     // quit if run by the _changed callbacks
2058     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2059         return;
2060     }
2062     // in turn, prevent callbacks from responding
2063     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2065     GtkAdjustment *adj = 0;
2067     gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2068     bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2070     if (!strcmp(name, "inkscape:randomized")) {
2071         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2072         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2073     } else if (!strcmp(name, "inkscape:rounded")) {
2074         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2075         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2076     } else if (!strcmp(name, "inkscape:flatsided")) {
2077         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2078         char const *flatsides = repr->attribute("inkscape:flatsided");
2079         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2080         if ( flatsides && !strcmp(flatsides,"false") ) {
2081             ege_select_one_action_set_active( flat_action, 1 );
2082             gtk_action_set_sensitive( prop_action, TRUE );
2083         } else {
2084             ege_select_one_action_set_active( flat_action, 0 );
2085             gtk_action_set_sensitive( prop_action, FALSE );
2086         }
2087     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2088         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2089         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2090         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2091         if (r2 < r1) {
2092             gtk_adjustment_set_value(adj, r2/r1);
2093         } else {
2094             gtk_adjustment_set_value(adj, r1/r2);
2095         }
2096     } else if (!strcmp(name, "sodipodi:sides")) {
2097         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2098         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2099     }
2101     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2105 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2107     NULL, /* child_added */
2108     NULL, /* child_removed */
2109     star_tb_event_attr_changed,
2110     NULL, /* content_changed */
2111     NULL  /* order_changed */
2112 };
2115 /**
2116  *  \param selection Should not be NULL.
2117  */
2118 static void
2119 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2121     int n_selected = 0;
2122     Inkscape::XML::Node *repr = NULL;
2124     purge_repr_listener( tbl, tbl );
2126     for (GSList const *items = selection->itemList();
2127          items != NULL;
2128          items = items->next)
2129     {
2130         if (SP_IS_STAR((SPItem *) items->data)) {
2131             n_selected++;
2132             repr = SP_OBJECT_REPR((SPItem *) items->data);
2133         }
2134     }
2136     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2138     if (n_selected == 0) {
2139         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2140     } else if (n_selected == 1) {
2141         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2143         if (repr) {
2144             g_object_set_data( tbl, "repr", repr );
2145             Inkscape::GC::anchor(repr);
2146             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2147             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2148         }
2149     } else {
2150         // FIXME: implement averaging of all parameters for multiple selected stars
2151         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2152         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2153     }
2157 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2159     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2160     // callbacks to lump all the changes for all selected objects in one undo step
2162     GtkAdjustment *adj = 0;
2164     // fixme: make settable in prefs!
2165     gint mag = 5;
2166     gdouble prop = 0.5;
2167     gboolean flat = FALSE;
2168     gdouble randomized = 0;
2169     gdouble rounded = 0;
2171     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2172     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2174     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2175     gtk_action_set_sensitive( sb2, !flat );
2177     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2178     gtk_adjustment_set_value(adj, mag);
2179     gtk_adjustment_value_changed(adj);
2181     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2182     gtk_adjustment_set_value(adj, prop);
2183     gtk_adjustment_value_changed(adj);
2185     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2186     gtk_adjustment_set_value(adj, rounded);
2187     gtk_adjustment_value_changed(adj);
2189     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2190     gtk_adjustment_set_value(adj, randomized);
2191     gtk_adjustment_value_changed(adj);
2195 void
2196 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2198     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2199     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2200     GtkWidget *l = gtk_label_new(NULL);
2201     gtk_label_set_markup(GTK_LABEL(l), title);
2202     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2203     if ( GTK_IS_TOOLBAR(tbl) ) {
2204         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2205     } else {
2206         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2207     }
2208     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2212 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2214     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2216     {
2217         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2218         ege_output_action_set_use_markup( act, TRUE );
2219         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2220         g_object_set_data( holder, "mode_action", act );
2221     }
2223     {
2224         EgeAdjustmentAction* eact = 0;
2225         gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
2226         bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
2228         /* Flatsided checkbox */
2229         {
2230             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2232             GtkTreeIter iter;
2233             gtk_list_store_append( model, &iter );
2234             gtk_list_store_set( model, &iter,
2235                                 0, _("Polygon"),
2236                                 1, _("Regular polygon (with one handle) instead of a star"),
2237                                 2, "star_flat",
2238                                 -1 );
2240             gtk_list_store_append( model, &iter );
2241             gtk_list_store_set( model, &iter,
2242                                 0, _("Star"),
2243                                 1, _("Star instead of a regular polygon (with one handle)"),
2244                                 2, "star_angled",
2245                                 -1 );
2247             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2248             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2249             g_object_set_data( holder, "flat_action", act );
2251             ege_select_one_action_set_appearance( act, "full" );
2252             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2253             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2254             ege_select_one_action_set_icon_column( act, 2 );
2255             ege_select_one_action_set_icon_size( act, secondarySize );
2256             ege_select_one_action_set_tooltip_column( act, 1  );
2258             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2259             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2260         }
2262         /* Magnitude */
2263         {
2264         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2265         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2266         eact = create_adjustment_action( "MagnitudeAction",
2267                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2268                                          "tools.shapes.star", "magnitude", 3,
2269                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2270                                          3, 1024, 1, 5,
2271                                          labels, values, G_N_ELEMENTS(labels),
2272                                          sp_stb_magnitude_value_changed,
2273                                          1.0, 0 );
2274         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2275         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2276         }
2278         /* Spoke ratio */
2279         {
2280         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2281         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2282         eact = create_adjustment_action( "SpokeAction",
2283                                          _("Spoke ratio"), _("Spoke ratio:"),
2284                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2285                                          // Base radius is the same for the closest handle.
2286                                          _("Base radius to tip radius ratio"),
2287                                          "tools.shapes.star", "proportion", 0.5,
2288                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2289                                          0.01, 1.0, 0.01, 0.1,
2290                                          labels, values, G_N_ELEMENTS(labels),
2291                                          sp_stb_proportion_value_changed );
2292         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2293         g_object_set_data( holder, "prop_action", eact );
2294         }
2296         if ( !isFlatSided ) {
2297             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2298         } else {
2299             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2300         }
2302         /* Roundedness */
2303         {
2304         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2305         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2306         eact = create_adjustment_action( "RoundednessAction",
2307                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2308                                          "tools.shapes.star", "rounded", 0.0,
2309                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2310                                          -10.0, 10.0, 0.01, 0.1,
2311                                          labels, values, G_N_ELEMENTS(labels),
2312                                          sp_stb_rounded_value_changed );
2313         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2314         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2315         }
2317         /* Randomization */
2318         {
2319         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2320         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2321         eact = create_adjustment_action( "RandomizationAction",
2322                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2323                                          "tools.shapes.star", "randomized", 0.0,
2324                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2325                                          -10.0, 10.0, 0.001, 0.01,
2326                                          labels, values, G_N_ELEMENTS(labels),
2327                                          sp_stb_randomized_value_changed, 0.1, 3 );
2328         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2329         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2330         }
2331     }
2333     {
2334         /* Reset */
2335         {
2336             GtkAction* act = gtk_action_new( "StarResetAction",
2337                                              _("Defaults"),
2338                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2339                                              GTK_STOCK_CLEAR );
2340             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2341             gtk_action_group_add_action( mainActions, act );
2342             gtk_action_set_sensitive( act, TRUE );
2343         }
2344     }
2346     sigc::connection *connection = new sigc::connection(
2347         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2348         );
2349     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2350     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2354 //########################
2355 //##       Rect         ##
2356 //########################
2358 static void sp_rtb_sensitivize( GObject *tbl )
2360     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2361     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2362     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2364     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2365         gtk_action_set_sensitive( not_rounded, FALSE );
2366     } else {
2367         gtk_action_set_sensitive( not_rounded, TRUE );
2368     }
2372 static void
2373 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2374                           void (*setter)(SPRect *, gdouble))
2376     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2378     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2379     SPUnit const *unit = tracker->getActiveUnit();
2381     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2382         prefs_set_double_attribute("tools.shapes.rect", value_name, sp_units_get_pixels(adj->value, *unit));
2383     }
2385     // quit if run by the attr_changed listener
2386     if (g_object_get_data( tbl, "freeze" )) {
2387         return;
2388     }
2390     // in turn, prevent listener from responding
2391     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2393     bool modmade = false;
2394     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2395     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2396         if (SP_IS_RECT(items->data)) {
2397             if (adj->value != 0) {
2398                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2399             } else {
2400                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2401             }
2402             modmade = true;
2403         }
2404     }
2406     sp_rtb_sensitivize( tbl );
2408     if (modmade) {
2409         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2410                                    _("Change rectangle"));
2411     }
2413     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2416 static void
2417 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2419     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2422 static void
2423 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2425     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2428 static void
2429 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2431     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2434 static void
2435 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2437     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2442 static void
2443 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2445     GtkAdjustment *adj = 0;
2447     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2448     gtk_adjustment_set_value(adj, 0.0);
2449     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2450     gtk_adjustment_value_changed(adj);
2452     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2453     gtk_adjustment_set_value(adj, 0.0);
2454     gtk_adjustment_value_changed(adj);
2456     sp_rtb_sensitivize( obj );
2459 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2460                                        gchar const */*old_value*/, gchar const */*new_value*/,
2461                                        bool /*is_interactive*/, gpointer data)
2463     GObject *tbl = G_OBJECT(data);
2465     // quit if run by the _changed callbacks
2466     if (g_object_get_data( tbl, "freeze" )) {
2467         return;
2468     }
2470     // in turn, prevent callbacks from responding
2471     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2473     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2474     SPUnit const *unit = tracker->getActiveUnit();
2476     gpointer item = g_object_get_data( tbl, "item" );
2477     if (item && SP_IS_RECT(item)) {
2478         {
2479             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2480             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2481             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2482         }
2484         {
2485             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2486             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2487             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2488         }
2490         {
2491             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2492             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2493             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2494         }
2496         {
2497             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2498             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2499             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2500         }
2501     }
2503     sp_rtb_sensitivize( tbl );
2505     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2509 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2510     NULL, /* child_added */
2511     NULL, /* child_removed */
2512     rect_tb_event_attr_changed,
2513     NULL, /* content_changed */
2514     NULL  /* order_changed */
2515 };
2517 /**
2518  *  \param selection should not be NULL.
2519  */
2520 static void
2521 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2523     int n_selected = 0;
2524     Inkscape::XML::Node *repr = NULL;
2525     SPItem *item = NULL;
2527     if ( g_object_get_data( tbl, "repr" ) ) {
2528         g_object_set_data( tbl, "item", NULL );
2529     }
2530     purge_repr_listener( tbl, tbl );
2532     for (GSList const *items = selection->itemList();
2533          items != NULL;
2534          items = items->next) {
2535         if (SP_IS_RECT((SPItem *) items->data)) {
2536             n_selected++;
2537             item = (SPItem *) items->data;
2538             repr = SP_OBJECT_REPR(item);
2539         }
2540     }
2542     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2544     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
2546     if (n_selected == 0) {
2547         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2549         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2550         gtk_action_set_sensitive(w, FALSE);
2551         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2552         gtk_action_set_sensitive(h, FALSE);
2554     } else if (n_selected == 1) {
2555         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2556         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
2558         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
2559         gtk_action_set_sensitive(w, TRUE);
2560         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
2561         gtk_action_set_sensitive(h, TRUE);
2563         if (repr) {
2564             g_object_set_data( tbl, "repr", repr );
2565             g_object_set_data( tbl, "item", item );
2566             Inkscape::GC::anchor(repr);
2567             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
2568             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
2569         }
2570     } else {
2571         // FIXME: implement averaging of all parameters for multiple selected
2572         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2573         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2574         sp_rtb_sensitivize( tbl );
2575     }
2579 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2581     EgeAdjustmentAction* eact = 0;
2582     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
2584     {
2585         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
2586         ege_output_action_set_use_markup( act, TRUE );
2587         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2588         g_object_set_data( holder, "mode_action", act );
2589     }
2591     // rx/ry units menu: create
2592     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
2593     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
2594     // fixme: add % meaning per cent of the width/height
2595     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
2596     g_object_set_data( holder, "tracker", tracker );
2598     /* W */
2599     {
2600         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2601         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2602         eact = create_adjustment_action( "RectWidthAction",
2603                                          _("Width"), _("W:"), _("Width of rectangle"),
2604                                          "tools.shapes.rect", "width", 0,
2605                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
2606                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2607                                          labels, values, G_N_ELEMENTS(labels),
2608                                          sp_rtb_width_value_changed );
2609         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2610         g_object_set_data( holder, "width_action", eact );
2611         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2612         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2613     }
2615     /* H */
2616     {
2617         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2618         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
2619         eact = create_adjustment_action( "RectHeightAction",
2620                                          _("Height"), _("H:"), _("Height of rectangle"),
2621                                          "tools.shapes.rect", "height", 0,
2622                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2623                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2624                                          labels, values, G_N_ELEMENTS(labels),
2625                                          sp_rtb_height_value_changed );
2626         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2627         g_object_set_data( holder, "height_action", eact );
2628         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2629         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2630     }
2632     /* rx */
2633     {
2634         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2635         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2636         eact = create_adjustment_action( "RadiusXAction",
2637                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
2638                                          "tools.shapes.rect", "rx", 0,
2639                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2640                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2641                                          labels, values, G_N_ELEMENTS(labels),
2642                                          sp_rtb_rx_value_changed);
2643         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2644         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2645     }
2647     /* ry */
2648     {
2649         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
2650         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
2651         eact = create_adjustment_action( "RadiusYAction",
2652                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
2653                                          "tools.shapes.rect", "ry", 0,
2654                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
2655                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
2656                                          labels, values, G_N_ELEMENTS(labels),
2657                                          sp_rtb_ry_value_changed);
2658         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
2659         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2660     }
2662     // add the units menu
2663     {
2664         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
2665         gtk_action_group_add_action( mainActions, act );
2666     }
2668     /* Reset */
2669     {
2670         InkAction* inky = ink_action_new( "RectResetAction",
2671                                           _("Not rounded"),
2672                                           _("Make corners sharp"),
2673                                           "squared_corner",
2674                                           secondarySize );
2675         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
2676         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
2677         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
2678         g_object_set_data( holder, "not_rounded", inky );
2679     }
2681     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
2682     sp_rtb_sensitivize( holder );
2684     sigc::connection *connection = new sigc::connection(
2685         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
2686         );
2687     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2688     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2691 //########################
2692 //##       3D Box       ##
2693 //########################
2695 // normalize angle so that it lies in the interval [0,360]
2696 static double box3d_normalize_angle (double a) {
2697     double angle = a + ((int) (a/360.0))*360;
2698     if (angle < 0) {
2699         angle += 360.0;
2700     }
2701     return angle;
2704 static void
2705 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
2706                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
2707     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
2708     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
2709     //       are reset).
2710     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
2712     if (is_infinite) {
2713         gtk_toggle_action_set_active(tact, TRUE);
2714         gtk_action_set_sensitive(act, TRUE);
2716         double angle = persp3d_get_infinite_angle(persp, axis);
2717         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
2718             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
2719         }
2720     } else {
2721         gtk_toggle_action_set_active(tact, FALSE);
2722         gtk_action_set_sensitive(act, FALSE);
2723     }
2726 static void
2727 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
2728     if (!persp_repr) {
2729         g_print ("No perspective given to box3d_resync_toolbar().\n");
2730         return;
2731     }
2733     GtkWidget *tbl = GTK_WIDGET(data);
2734     GtkAdjustment *adj = 0;
2735     GtkAction *act = 0;
2736     GtkToggleAction *tact = 0;
2737     Persp3D *persp = persp3d_get_from_repr(persp_repr);
2738     {
2739         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
2740         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
2741         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
2743         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
2744     }
2745     {
2746         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
2747         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
2748         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
2750         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
2751     }
2752     {
2753         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
2754         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
2755         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
2757         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
2758     }
2761 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
2762                                                   gchar const */*old_value*/, gchar const */*new_value*/,
2763                                                   bool /*is_interactive*/, gpointer data)
2765     GtkWidget *tbl = GTK_WIDGET(data);
2767     // quit if run by the attr_changed listener
2768     // note: it used to work without the differently called freeze_ attributes (here and in
2769     //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
2770     if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
2771         return;
2772     }
2774     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
2775     // sp_document_maybe_done() when the document is undo insensitive)
2776     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
2778     // TODO: Only update the appropriate part of the toolbar
2779 //    if (!strcmp(name, "inkscape:vp_z")) {
2780         box3d_resync_toolbar(repr, G_OBJECT(tbl));
2781 //    }
2783     Persp3D *persp = persp3d_get_from_repr(repr);
2784     persp3d_update_box_reprs(persp);
2786     g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
2789 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
2791     NULL, /* child_added */
2792     NULL, /* child_removed */
2793     box3d_persp_tb_event_attr_changed,
2794     NULL, /* content_changed */
2795     NULL  /* order_changed */
2796 };
2798 /**
2799  *  \param selection Should not be NULL.
2800  */
2801 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
2802 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
2803 static void
2804 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2806     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
2807     // disable the angle entry fields for this direction (otherwise entering a value in them should only
2808     // update the perspectives with infinite VPs and leave the other ones untouched).
2810     Inkscape::XML::Node *persp_repr = NULL;
2811     purge_repr_listener(tbl, tbl);
2813     SPItem *item = selection->singleItem();
2814     if (item && SP_IS_BOX3D(item)) {
2815         // FIXME: Also deal with multiple selected boxes
2816         SPBox3D *box = SP_BOX3D(item);
2817         Persp3D *persp = box3d_get_perspective(box);
2818         persp_repr = SP_OBJECT_REPR(persp);
2819         if (persp_repr) {
2820             g_object_set_data(tbl, "repr", persp_repr);
2821             Inkscape::GC::anchor(persp_repr);
2822             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
2823             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
2824         }
2826         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
2827         prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
2829         box3d_resync_toolbar(persp_repr, tbl);
2830     }
2833 static void
2834 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
2836     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2837     SPDocument *document = sp_desktop_document(desktop);
2839     // quit if run by the attr_changed listener
2840     // note: it used to work without the differently called freeze_ attributes (here and in
2841     //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
2842     if (g_object_get_data( dataKludge, "freeze_attr" )) {
2843         return;
2844     }
2846     // in turn, prevent listener from responding
2847     g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
2849     //Persp3D *persp = document->current_persp3d;
2850     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
2851     if (sel_persps.empty()) {
2852         // this can happen when the document is created; we silently ignore it
2853         return;
2854     }
2855     Persp3D *persp = sel_persps.front();
2857     persp->tmat.set_infinite_direction (axis, adj->value);
2858     SP_OBJECT(persp)->updateRepr();
2860     // TODO: use the correct axis here, too
2861     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
2863     g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
2867 static void
2868 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2870     box3d_angle_value_changed(adj, dataKludge, Proj::X);
2873 static void
2874 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2876     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
2879 static void
2880 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
2882     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
2886 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
2888     // TODO: Take all selected perspectives into account
2889     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
2890     if (sel_persps.empty()) {
2891         // this can happen when the document is created; we silently ignore it
2892         return;
2893     }
2894     Persp3D *persp = sel_persps.front();
2896     bool set_infinite = gtk_toggle_action_get_active(act);
2897     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
2900 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2902     box3d_vp_state_changed(act, box3d_angle, Proj::X);
2905 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2907     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
2910 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
2912     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
2915 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2917     EgeAdjustmentAction* eact = 0;
2918     SPDocument *document = sp_desktop_document (desktop);
2919     Persp3D *persp = document->current_persp3d;
2921     EgeAdjustmentAction* box3d_angle_x = 0;
2922     EgeAdjustmentAction* box3d_angle_y = 0;
2923     EgeAdjustmentAction* box3d_angle_z = 0;
2925     /* Angle X */
2926     {
2927         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2928         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2929         eact = create_adjustment_action( "3DBoxAngleXAction",
2930                                          _("Angle in X direction"), _("Angle X:"),
2931                                          // Translators: PL is short for 'perspective line'
2932                                          _("Angle of PLs in X direction"),
2933                                          "tools.shapes.3dbox", "box3d_angle_x", 30,
2934                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
2935                                          -360.0, 360.0, 1.0, 10.0,
2936                                          labels, values, G_N_ELEMENTS(labels),
2937                                          box3d_angle_x_value_changed );
2938         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2939         g_object_set_data( holder, "box3d_angle_x_action", eact );
2940         box3d_angle_x = eact;
2941     }
2943     if (!persp3d_VP_is_finite(persp, Proj::X)) {
2944         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2945     } else {
2946         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2947     }
2950     /* VP X state */
2951     {
2952         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
2953                                                       // Translators: VP is short for 'vanishing point'
2954                                                       _("State of VP in X direction"),
2955                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
2956                                                       "toggle_vp_x",
2957                                                       Inkscape::ICON_SIZE_DECORATION );
2958         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2959         g_object_set_data( holder, "box3d_vp_x_state_action", act );
2960         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
2961         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2962         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
2963     }
2965     /* Angle Y */
2966     {
2967         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
2968         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
2969         eact = create_adjustment_action( "3DBoxAngleYAction",
2970                                          _("Angle in Y direction"), _("Angle Y:"),
2971                                          // Translators: PL is short for 'perspective line'
2972                                          _("Angle of PLs in Y direction"),
2973                                          "tools.shapes.3dbox", "box3d_angle_y", 30,
2974                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2975                                          -360.0, 360.0, 1.0, 10.0,
2976                                          labels, values, G_N_ELEMENTS(labels),
2977                                          box3d_angle_y_value_changed );
2978         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2979         g_object_set_data( holder, "box3d_angle_y_action", eact );
2980         box3d_angle_y = eact;
2981     }
2983     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
2984         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2985     } else {
2986         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2987     }
2989     /* VP Y state */
2990     {
2991         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
2992                                                       // Translators: VP is short for 'vanishing point'
2993                                                       _("State of VP in Y direction"),
2994                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
2995                                                       "toggle_vp_y",
2996                                                       Inkscape::ICON_SIZE_DECORATION );
2997         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2998         g_object_set_data( holder, "box3d_vp_y_state_action", act );
2999         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3000         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3001         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
3002     }
3004     /* Angle Z */
3005     {
3006         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3007         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3008         eact = create_adjustment_action( "3DBoxAngleZAction",
3009                                          _("Angle in Z direction"), _("Angle Z:"),
3010                                          // Translators: PL is short for 'perspective line'
3011                                          _("Angle of PLs in Z direction"),
3012                                          "tools.shapes.3dbox", "box3d_angle_z", 30,
3013                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3014                                          -360.0, 360.0, 1.0, 10.0,
3015                                          labels, values, G_N_ELEMENTS(labels),
3016                                          box3d_angle_z_value_changed );
3017         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3018         g_object_set_data( holder, "box3d_angle_z_action", eact );
3019         box3d_angle_z = eact;
3020     }
3022     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3023         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3024     } else {
3025         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3026     }
3028     /* VP Z state */
3029     {
3030         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3031                                                       // Translators: VP is short for 'vanishing point'
3032                                                       _("State of VP in Z direction"),
3033                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3034                                                       "toggle_vp_z",
3035                                                       Inkscape::ICON_SIZE_DECORATION );
3036         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3037         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3038         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3039         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3040         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
3041     }
3043     sigc::connection *connection = new sigc::connection(
3044         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3045        );
3046     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3047     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3050 //########################
3051 //##       Spiral       ##
3052 //########################
3054 static void
3055 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
3057     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3059     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3060         prefs_set_double_attribute("tools.shapes.spiral", value_name, adj->value);
3061     }
3063     // quit if run by the attr_changed listener
3064     if (g_object_get_data( tbl, "freeze" )) {
3065         return;
3066     }
3068     // in turn, prevent listener from responding
3069     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3071     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
3073     bool modmade = false;
3074     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3075          items != NULL;
3076          items = items->next)
3077     {
3078         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3079             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3080             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3081             SP_OBJECT((SPItem *) items->data)->updateRepr();
3082             modmade = true;
3083         }
3084     }
3086     g_free(namespaced_name);
3088     if (modmade) {
3089         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3090                                    _("Change spiral"));
3091     }
3093     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3096 static void
3097 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3099     sp_spl_tb_value_changed(adj, tbl, "revolution");
3102 static void
3103 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3105     sp_spl_tb_value_changed(adj, tbl, "expansion");
3108 static void
3109 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3111     sp_spl_tb_value_changed(adj, tbl, "t0");
3114 static void
3115 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3117     GtkWidget *tbl = GTK_WIDGET(obj);
3119     GtkAdjustment *adj;
3121     // fixme: make settable
3122     gdouble rev = 5;
3123     gdouble exp = 1.0;
3124     gdouble t0 = 0.0;
3126     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3127     gtk_adjustment_set_value(adj, rev);
3128     gtk_adjustment_value_changed(adj);
3130     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3131     gtk_adjustment_set_value(adj, exp);
3132     gtk_adjustment_value_changed(adj);
3134     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3135     gtk_adjustment_set_value(adj, t0);
3136     gtk_adjustment_value_changed(adj);
3138     spinbutton_defocus(GTK_OBJECT(tbl));
3142 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3143                                          gchar const */*old_value*/, gchar const */*new_value*/,
3144                                          bool /*is_interactive*/, gpointer data)
3146     GtkWidget *tbl = GTK_WIDGET(data);
3148     // quit if run by the _changed callbacks
3149     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3150         return;
3151     }
3153     // in turn, prevent callbacks from responding
3154     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3156     GtkAdjustment *adj;
3157     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3158     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3160     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3161     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3163     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3164     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3166     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3170 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3171     NULL, /* child_added */
3172     NULL, /* child_removed */
3173     spiral_tb_event_attr_changed,
3174     NULL, /* content_changed */
3175     NULL  /* order_changed */
3176 };
3178 static void
3179 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3181     int n_selected = 0;
3182     Inkscape::XML::Node *repr = NULL;
3184     purge_repr_listener( tbl, tbl );
3186     for (GSList const *items = selection->itemList();
3187          items != NULL;
3188          items = items->next)
3189     {
3190         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3191             n_selected++;
3192             repr = SP_OBJECT_REPR((SPItem *) items->data);
3193         }
3194     }
3196     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3198     if (n_selected == 0) {
3199         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3200     } else if (n_selected == 1) {
3201         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3203         if (repr) {
3204             g_object_set_data( tbl, "repr", repr );
3205             Inkscape::GC::anchor(repr);
3206             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3207             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3208         }
3209     } else {
3210         // FIXME: implement averaging of all parameters for multiple selected
3211         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3212         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3213     }
3217 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3219     EgeAdjustmentAction* eact = 0;
3220     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3222     {
3223         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3224         ege_output_action_set_use_markup( act, TRUE );
3225         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3226         g_object_set_data( holder, "mode_action", act );
3227     }
3229     /* Revolution */
3230     {
3231         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3232         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3233         eact = create_adjustment_action( "SpiralRevolutionAction",
3234                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3235                                          "tools.shapes.spiral", "revolution", 3.0,
3236                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3237                                          0.01, 1024.0, 0.1, 1.0,
3238                                          labels, values, G_N_ELEMENTS(labels),
3239                                          sp_spl_tb_revolution_value_changed, 1, 2);
3240         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3241     }
3243     /* Expansion */
3244     {
3245         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3246         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3247         eact = create_adjustment_action( "SpiralExpansionAction",
3248                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3249                                          "tools.shapes.spiral", "expansion", 1.0,
3250                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3251                                          0.0, 1000.0, 0.01, 1.0,
3252                                          labels, values, G_N_ELEMENTS(labels),
3253                                          sp_spl_tb_expansion_value_changed);
3254         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3255     }
3257     /* T0 */
3258     {
3259         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3260         gdouble values[] = {0, 0.5, 0.9};
3261         eact = create_adjustment_action( "SpiralT0Action",
3262                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3263                                          "tools.shapes.spiral", "t0", 0.0,
3264                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3265                                          0.0, 0.999, 0.01, 1.0,
3266                                          labels, values, G_N_ELEMENTS(labels),
3267                                          sp_spl_tb_t0_value_changed);
3268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3269     }
3271     /* Reset */
3272     {
3273         InkAction* inky = ink_action_new( "SpiralResetAction",
3274                                           _("Defaults"),
3275                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3276                                           GTK_STOCK_CLEAR,
3277                                           secondarySize );
3278         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3279         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3280     }
3283     sigc::connection *connection = new sigc::connection(
3284         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3285         );
3286     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3287     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3290 //########################
3291 //##     Pen/Pencil     ##
3292 //########################
3294 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3295 static char const *
3296 freehand_tool_name(GObject *dataKludge)
3298     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3299     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3300              ? "tools.freehand.pen"
3301              : "tools.freehand.pencil" );
3304 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3306     gint mode = ege_select_one_action_get_active(act);
3308     prefs_set_int_attribute(freehand_tool_name(tbl), "freehand-mode", mode);
3310     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3312     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3313     // preparatory work here
3314     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3315         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3316         sp_pen_context_set_polyline_mode(pc);
3317     }
3320 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3322     /* Freehand mode toggle buttons */
3323     {
3324         guint freehandMode = prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "freehand-mode", 0);
3325         Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3327         {
3328             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3330             GtkTreeIter iter;
3331             gtk_list_store_append( model, &iter );
3332             gtk_list_store_set( model, &iter,
3333                                 0, _("Bezier"),
3334                                 1, _("Create regular Bezier path"),
3335                                 2, "bezier_mode",
3336                                 -1 );
3338             gtk_list_store_append( model, &iter );
3339             gtk_list_store_set( model, &iter,
3340                                 0, _("Spiro"),
3341                                 1, _("Create Spiro path"),
3342                                 2, "spiro_splines_mode",
3343                                 -1 );
3345             if (!tool_is_pencil) {
3346                 gtk_list_store_append( model, &iter );
3347                 gtk_list_store_set( model, &iter,
3348                                     0, _("Zigzag"),
3349                                     1, _("Create a sequence of straight line segments"),
3350                                     2, "polylines_mode",
3351                                     -1 );
3353                 gtk_list_store_append( model, &iter );
3354                 gtk_list_store_set( model, &iter,
3355                                     0, _("Paraxial"),
3356                                     1, _("Create a sequence of paraxial line segments"),
3357                                     2, "paraxial_lines_mode",
3358                                     -1 );
3359             }
3361             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3362                                                                 "FreehandModeActionPencil" :
3363                                                                 "FreehandModeActionPen",
3364                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3365             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3367             ege_select_one_action_set_appearance( act, "full" );
3368             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3369             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3370             ege_select_one_action_set_icon_column( act, 2 );
3371             ege_select_one_action_set_icon_size( act, secondarySize );
3372             ege_select_one_action_set_tooltip_column( act, 1  );
3374             ege_select_one_action_set_active( act, freehandMode);
3375             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3376         }
3377     }
3380 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3381     gint shape = ege_select_one_action_get_active( act );
3382     prefs_set_int_attribute(freehand_tool_name(dataKludge), "shape", shape);
3385 /**
3386  * \brief Generate the list of freehand advanced shape option entries.
3387  */
3388 GList * freehand_shape_dropdown_items_list() {
3389     GList *glist = NULL;
3391     glist = g_list_append (glist, _("None"));
3392     glist = g_list_append (glist, _("Triangle in"));
3393     glist = g_list_append (glist, _("Triangle out"));
3394     glist = g_list_append (glist, _("Ellipse"));
3395     glist = g_list_append (glist, _("From clipboard"));
3397     return glist;
3400 static void
3401 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3402     /*advanced shape options */
3403     {
3404         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3406         GList* items = 0;
3407         gint count = 0;
3408         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3409         {
3410             GtkTreeIter iter;
3411             gtk_list_store_append( model, &iter );
3412             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3413             count++;
3414         }
3415         g_list_free( items );
3416         items = 0;
3417         EgeSelectOneAction* act1 = ege_select_one_action_new(
3418             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3419             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3420         g_object_set( act1, "short_label", _("Shape:"), NULL );
3421         ege_select_one_action_set_appearance( act1, "compact" );
3422         ege_select_one_action_set_active( act1, prefs_get_int_attribute(tool_is_pencil ? "tools.freehand.pencil" : "tools.freehand.pen", "shape", 0) );
3423         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3424         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3425         g_object_set_data( holder, "shape_action", act1 );
3426     }
3429 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3431     sp_add_freehand_mode_toggle(mainActions, holder, false);
3432     freehand_add_advanced_shape_options(mainActions, holder, false);
3436 static void
3437 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3439     GtkWidget *tbl = GTK_WIDGET(obj);
3441     GtkAdjustment *adj;
3443     // fixme: make settable
3444     gdouble tolerance = 4;
3446     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3447     gtk_adjustment_set_value(adj, tolerance);
3448     gtk_adjustment_value_changed(adj);
3450     spinbutton_defocus(GTK_OBJECT(tbl));
3453 static void
3454 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3457     // quit if run by the attr_changed listener
3458     if (g_object_get_data( tbl, "freeze" )) {
3459         return;
3460     }
3461     // in turn, prevent listener from responding
3462     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3463     prefs_set_double_attribute("tools.freehand.pencil",
3464                                "tolerance", adj->value);
3465     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3471 static void
3472 sp_pencil_tb_tolerance_value_changed_external(Inkscape::XML::Node */*repr*/,
3473                                               const gchar */*key*/,
3474                                               const gchar */*oldval*/,
3475                                               const gchar */*newval*/,
3476                                               bool /*is_interactive*/,
3477                                               void * data)
3479     GObject* tbl = G_OBJECT(data);
3480     if (g_object_get_data( tbl, "freeze" )) {
3481         return;
3482     }
3484     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3486     GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl,
3487                                                             "tolerance");
3489     double v = prefs_get_double_attribute("tools.freehand.pencil",
3490                                             "tolerance", adj->value);
3491     gtk_adjustment_set_value(adj, v);
3492     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3496 static Inkscape::XML::NodeEventVector pencil_node_events =
3498     NULL,
3499     NULL,
3500     sp_pencil_tb_tolerance_value_changed_external,
3501     NULL,
3502     NULL,
3503 };
3506 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3508     sp_add_freehand_mode_toggle(mainActions, holder, true);
3510     EgeAdjustmentAction* eact = 0;
3512     /* Tolerance */
3513     {
3514         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3515         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3516         eact = create_adjustment_action( "PencilToleranceAction",
3517                                          _("Smoothing:"), _("Smoothing: "),
3518                  _("How much smoothing (simplifying) is applied to the line"),
3519                                          "tools.freehand.pencil", "tolerance",
3520                                          3.0,
3521                                          GTK_WIDGET(desktop->canvas), NULL,
3522                                          holder, TRUE, "altx-pencil",
3523                                          1, 100.0, 0.5, 0,
3524                                          labels, values, G_N_ELEMENTS(labels),
3525                                          sp_pencil_tb_tolerance_value_changed,
3526                                          1, 2);
3527         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3528         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3530         Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE,
3531                                                       "tools.freehand.pencil");
3532         repr->addListener(&pencil_node_events, G_OBJECT(holder));
3533         g_object_set_data(G_OBJECT(holder), "repr", repr);
3535     }
3537     /* advanced shape options */
3538     freehand_add_advanced_shape_options(mainActions, holder, true);
3540     /* Reset */
3541     {
3542         InkAction* inky = ink_action_new( "PencilResetAction",
3543                                           _("Defaults"),
3544                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3545                                           GTK_STOCK_CLEAR,
3546                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
3547         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
3548         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3549     }
3551     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3556 //########################
3557 //##       Tweak        ##
3558 //########################
3560 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3562     prefs_set_double_attribute( "tools.tweak", "width", adj->value * 0.01 );
3565 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3567     prefs_set_double_attribute( "tools.tweak", "force", adj->value * 0.01 );
3570 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
3572     prefs_set_int_attribute( "tools.tweak", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3575 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
3577     int mode = ege_select_one_action_get_active( act );
3578     prefs_set_int_attribute("tools.tweak", "mode", mode);
3580     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
3581     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
3582     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
3583     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
3584     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
3585     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
3586     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
3587         if (doh) gtk_action_set_sensitive (doh, TRUE);
3588         if (dos) gtk_action_set_sensitive (dos, TRUE);
3589         if (dol) gtk_action_set_sensitive (dol, TRUE);
3590         if (doo) gtk_action_set_sensitive (doo, TRUE);
3591         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
3592         if (fid) gtk_action_set_sensitive (fid, FALSE);
3593     } else {
3594         if (doh) gtk_action_set_sensitive (doh, FALSE);
3595         if (dos) gtk_action_set_sensitive (dos, FALSE);
3596         if (dol) gtk_action_set_sensitive (dol, FALSE);
3597         if (doo) gtk_action_set_sensitive (doo, FALSE);
3598         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
3599         if (fid) gtk_action_set_sensitive (fid, TRUE);
3600     }
3603 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
3605     prefs_set_double_attribute( "tools.tweak", "fidelity", adj->value * 0.01 );
3608 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
3609     bool show = gtk_toggle_action_get_active( act );
3610     prefs_set_int_attribute ("tools.tweak", "doh",  show ? 1 : 0);
3612 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
3613     bool show = gtk_toggle_action_get_active( act );
3614     prefs_set_int_attribute ("tools.tweak", "dos",  show ? 1 : 0);
3616 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
3617     bool show = gtk_toggle_action_get_active( act );
3618     prefs_set_int_attribute ("tools.tweak", "dol",  show ? 1 : 0);
3620 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
3621     bool show = gtk_toggle_action_get_active( act );
3622     prefs_set_int_attribute ("tools.tweak", "doo",  show ? 1 : 0);
3625 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3627     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
3629     {
3630         /* Width */
3631         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
3632         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
3633         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
3634                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
3635                                                               "tools.tweak", "width", 15,
3636                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
3637                                                               1, 100, 1.0, 0.0,
3638                                                               labels, values, G_N_ELEMENTS(labels),
3639                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
3640         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3641         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3642         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3643     }
3646     {
3647         /* Force */
3648         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
3649         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
3650         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
3651                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
3652                                                               "tools.tweak", "force", 20,
3653                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
3654                                                               1, 100, 1.0, 0.0,
3655                                                               labels, values, G_N_ELEMENTS(labels),
3656                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
3657         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3658         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3659         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3660     }
3662     /* Mode */
3663     {
3664         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3666         GtkTreeIter iter;
3667         gtk_list_store_append( model, &iter );
3668         gtk_list_store_set( model, &iter,
3669                             0, _("Push mode"),
3670                             1, _("Push parts of paths in any direction"),
3671                             2, "tweak_push_mode",
3672                             -1 );
3674         gtk_list_store_append( model, &iter );
3675         gtk_list_store_set( model, &iter,
3676                             0, _("Shrink mode"),
3677                             1, _("Shrink (inset) parts of paths"),
3678                             2, "tweak_shrink_mode",
3679                             -1 );
3681         gtk_list_store_append( model, &iter );
3682         gtk_list_store_set( model, &iter,
3683                             0, _("Grow mode"),
3684                             1, _("Grow (outset) parts of paths"),
3685                             2, "tweak_grow_mode",
3686                             -1 );
3688         gtk_list_store_append( model, &iter );
3689         gtk_list_store_set( model, &iter,
3690                             0, _("Attract mode"),
3691                             1, _("Attract parts of paths towards cursor"),
3692                             2, "tweak_attract_mode",
3693                             -1 );
3695         gtk_list_store_append( model, &iter );
3696         gtk_list_store_set( model, &iter,
3697                             0, _("Repel mode"),
3698                             1, _("Repel parts of paths from cursor"),
3699                             2, "tweak_repel_mode",
3700                             -1 );
3702         gtk_list_store_append( model, &iter );
3703         gtk_list_store_set( model, &iter,
3704                             0, _("Roughen mode"),
3705                             1, _("Roughen parts of paths"),
3706                             2, "tweak_roughen_mode",
3707                             -1 );
3709         gtk_list_store_append( model, &iter );
3710         gtk_list_store_set( model, &iter,
3711                             0, _("Color paint mode"),
3712                             1, _("Paint the tool's color upon selected objects"),
3713                             2, "tweak_colorpaint_mode",
3714                             -1 );
3716         gtk_list_store_append( model, &iter );
3717         gtk_list_store_set( model, &iter,
3718                             0, _("Color jitter mode"),
3719                             1, _("Jitter the colors of selected objects"),
3720                             2, "tweak_colorjitter_mode",
3721                             -1 );
3723         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
3724         g_object_set( act, "short_label", _("Mode:"), NULL );
3725         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3726         g_object_set_data( holder, "mode_action", act );
3728         ege_select_one_action_set_appearance( act, "full" );
3729         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3730         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3731         ege_select_one_action_set_icon_column( act, 2 );
3732         ege_select_one_action_set_icon_size( act, secondarySize );
3733         ege_select_one_action_set_tooltip_column( act, 1  );
3735         gint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3736         ege_select_one_action_set_active( act, mode );
3737         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
3739         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
3740     }
3742     guint mode = prefs_get_int_attribute("tools.tweak", "mode", 0);
3744     {
3745         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
3746         ege_output_action_set_use_markup( act, TRUE );
3747         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3748         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3749             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3750         g_object_set_data( holder, "tweak_channels_label", act);
3751     }
3753     {
3754         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
3755                                                       _("Hue"),
3756                                                       _("In color mode, act on objects' hue"),
3757                                                       NULL,
3758                                                       Inkscape::ICON_SIZE_DECORATION );
3759         //TRANSLATORS:  "H" here stands for hue
3760         g_object_set( act, "short_label", _("H"), NULL );
3761         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3762         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
3763         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doh", 1 ) );
3764         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3765             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3766         g_object_set_data( holder, "tweak_doh", act);
3767     }
3768     {
3769         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
3770                                                       _("Saturation"),
3771                                                       _("In color mode, act on objects' saturation"),
3772                                                       NULL,
3773                                                       Inkscape::ICON_SIZE_DECORATION );
3774         //TRANSLATORS: "S" here stands for Saturation
3775         g_object_set( act, "short_label", _("S"), NULL );
3776         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3777         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
3778         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dos", 1 ) );
3779         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3780             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3781         g_object_set_data( holder, "tweak_dos", act );
3782     }
3783     {
3784         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
3785                                                       _("Lightness"),
3786                                                       _("In color mode, act on objects' lightness"),
3787                                                       NULL,
3788                                                       Inkscape::ICON_SIZE_DECORATION );
3789         //TRANSLATORS: "L" here stands for Lightness
3790         g_object_set( act, "short_label", _("L"), NULL );
3791         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3792         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
3793         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "dol", 1 ) );
3794         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3795             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3796         g_object_set_data( holder, "tweak_dol", act );
3797     }
3798     {
3799         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
3800                                                       _("Opacity"),
3801                                                       _("In color mode, act on objects' opacity"),
3802                                                       NULL,
3803                                                       Inkscape::ICON_SIZE_DECORATION );
3804         //TRANSLATORS: "O" here stands for Opacity
3805         g_object_set( act, "short_label", _("O"), NULL );
3806         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3807         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
3808         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "doo", 1 ) );
3809         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
3810             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
3811         g_object_set_data( holder, "tweak_doo", act );
3812     }
3814     {   /* Fidelity */
3815         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
3816         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
3817         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
3818                                                               _("Fidelity"), _("Fidelity:"),
3819                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
3820                                                               "tools.tweak", "fidelity", 50,
3821                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
3822                                                               1, 100, 1.0, 10.0,
3823                                                               labels, values, G_N_ELEMENTS(labels),
3824                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
3825         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3826         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3827         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
3828             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
3829         g_object_set_data( holder, "tweak_fidelity", eact );
3830     }
3833     /* Use Pressure button */
3834     {
3835         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
3836                                                       _("Pressure"),
3837                                                       _("Use the pressure of the input device to alter the force of tweak action"),
3838                                                       "use_pressure",
3839                                                       Inkscape::ICON_SIZE_DECORATION );
3840         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3841         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
3842         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.tweak", "usepressure", 1 ) );
3843     }
3848 //########################
3849 //##     Calligraphy    ##
3850 //########################
3851 static void update_presets_list (GObject *tbl)
3853     if (g_object_get_data(tbl, "presets_blocked"))
3854         return;
3856     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
3857     if (!sel) {
3858         ege_select_one_action_set_active(sel, 0);
3859         return;
3860     }
3862     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
3864     for (int i = 1; i <= total_prefs; i++) {
3865         gchar *preset_path = get_pref_nth_child("tools.calligraphic.preset", i);
3866         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
3868         bool match = true;
3870         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
3871               iter; 
3872               ++iter ) {
3873             const gchar *attr_name = g_quark_to_string(iter->key);
3874             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
3875                 continue;
3876             void *widget = g_object_get_data(tbl, attr_name);
3877             if (widget) {
3878                 if (GTK_IS_ADJUSTMENT(widget)) {
3879                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3880                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
3881                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
3882                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
3883                         match = false;
3884                         break;
3885                     }
3886                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
3887                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
3888                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
3889                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
3890                     if (gtk_toggle_action_get_active(toggle) != v) {
3891                         match = false;
3892                         break;
3893                     }
3894                 } 
3895             } 
3896         }
3898         if (match) {
3899             // newly added item is at the same index as the 
3900             // save command, so we need to change twice for it to take effect
3901             ege_select_one_action_set_active(sel, 0);
3902             ege_select_one_action_set_active(sel, i);
3903             return;
3904         }
3905     }
3907     // no match found
3908     ege_select_one_action_set_active(sel, 0);
3911 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
3913     prefs_set_double_attribute( "tools.calligraphic", "mass", adj->value * 0.01 );
3914     update_presets_list(tbl);
3917 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
3919     prefs_set_double_attribute( "tools.calligraphic", "wiggle", adj->value * 0.01 );
3920     update_presets_list(tbl);
3923 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
3925     prefs_set_double_attribute( "tools.calligraphic", "angle", adj->value );
3926     update_presets_list(tbl);
3929 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
3931     prefs_set_double_attribute( "tools.calligraphic", "width", adj->value * 0.01 );
3932     update_presets_list(tbl);
3935 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
3937     prefs_set_double_attribute("tools.calligraphic", "thinning", adj->value * 0.01 );
3938     update_presets_list(tbl);
3941 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
3943     prefs_set_double_attribute( "tools.calligraphic", "flatness", adj->value * 0.01);
3944     update_presets_list(tbl);
3947 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
3949     prefs_set_double_attribute( "tools.calligraphic", "tremor", adj->value * 0.01 );
3950     update_presets_list(tbl);
3953 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
3955     prefs_set_double_attribute( "tools.calligraphic", "cap_rounding", adj->value );
3956     update_presets_list(tbl);
3959 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
3961     prefs_set_int_attribute( "tools.calligraphic", "usepressure", gtk_toggle_action_get_active( act ) ? 1 : 0);
3962     update_presets_list(tbl);
3965 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
3967     prefs_set_int_attribute( "tools.calligraphic", "tracebackground", gtk_toggle_action_get_active( act ) ? 1 : 0);
3968     update_presets_list(tbl);
3971 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
3973     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
3974     prefs_set_int_attribute( "tools.calligraphic", "usetilt", gtk_toggle_action_get_active( act ) ? 1 : 0 );
3975     update_presets_list(tbl);
3976     if (calligraphy_angle )
3977         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
3981 static gchar const *const widget_names[] = {
3982     "width",
3983     "mass",
3984     "wiggle",
3985     "angle",
3986     "thinning",
3987     "tremor",
3988     "flatness",
3989     "cap_rounding",
3990     "usepressure",
3991     "tracebackground",
3992     "usetilt"
3993 };
3996 static void sp_dcc_build_presets_list(GObject *tbl) 
3998     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4000     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4001     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4002     gtk_list_store_clear (model);
4004     {
4005         GtkTreeIter iter;
4006         gtk_list_store_append( model, &iter );
4007         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4008     }
4010     //TODO: switch back to prefs API
4011     Inkscape::XML::Node *repr = inkscape_get_repr(INKSCAPE, "tools.calligraphic.preset" );
4012     Inkscape::XML::Node *child_repr = sp_repr_children(repr);
4013     int ii=1;
4014     while (child_repr) {
4015         GtkTreeIter iter;
4016         char *preset_name = (char *) child_repr->attribute("name");
4017         gtk_list_store_append( model, &iter );
4018         gtk_list_store_set( model, &iter, 0, preset_name, 1, ++ii, -1 );
4019         child_repr = sp_repr_next(child_repr);
4020     }
4022     {
4023         GtkTreeIter iter;
4024         gtk_list_store_append( model, &iter );
4025         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4026         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4027     }
4029     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4031     update_presets_list (tbl);
4034 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4036     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4037     if (! desktop) return;
4039     if (g_object_get_data(tbl, "presets_blocked")) 
4040         return;
4042     Inkscape::UI::Dialogs::CalligraphicProfileDialog::show(desktop);
4043     if ( ! Inkscape::UI::Dialogs::CalligraphicProfileDialog::applied()) {
4044         // dialog cancelled
4045         update_presets_list (tbl);
4046         return;
4047     }
4048     Glib::ustring profile_name = Inkscape::UI::Dialogs::CalligraphicProfileDialog::getProfileName();
4050     if (!profile_name.c_str() || *profile_name.c_str() == 0) {
4051         // empty name entered
4052         update_presets_list (tbl);
4053         return;
4054     }
4056     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4058     int new_index = -1;
4059     gchar *pref_path = NULL;
4060     int total_prefs = pref_path_number_of_children("tools.calligraphic.preset");
4062     for (int i = 1; i <= total_prefs; i++) {
4063         gchar *path = get_pref_nth_child("tools.calligraphic.preset", i);
4064         const gchar *name = prefs_get_string_attribute(path, "name");
4065         if (name && !strcmp(name, profile_name.c_str())) {
4066             // we already have preset with this name, replace its values
4067             new_index = i;
4068             pref_path = g_strdup(path);
4069             break;
4070         }
4071     }
4073     if (new_index == -1) {
4074         // no preset with this name, create 
4075         new_index = total_prefs + 1;
4076         gchar *profile_id = g_strdup_printf("dcc%d", new_index);
4077         pref_path = create_pref("tools.calligraphic.preset", profile_id);
4078         g_free(profile_id);
4079     }
4081     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4082         gchar const *const widget_name = widget_names[i];
4083         void *widget = g_object_get_data(tbl, widget_name);
4084         if (widget) {
4085             if (GTK_IS_ADJUSTMENT(widget)) {
4086                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4087                 double v = gtk_adjustment_get_value(adj);
4088                 prefs_set_double_attribute(pref_path, widget_name, v);
4089                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4090             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4091                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4092                 int v = gtk_toggle_action_get_active(toggle);
4093                 prefs_set_int_attribute(pref_path, widget_name, v);
4094                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4095             } else {
4096                g_warning("Unknown widget type for preset: %s\n", widget_name);
4097             }
4098         } else {
4099             g_warning("Bad key when writing preset: %s\n", widget_name);
4100         }
4101     }
4102     prefs_set_string_attribute(pref_path, "name", profile_name.c_str());
4104     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4106     sp_dcc_build_presets_list (tbl);
4108     g_free(pref_path);
4112 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4114     gint preset_index = ege_select_one_action_get_active( act );
4115     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4117     if (preset_index == save_presets_index) {
4118         // this is the Save command
4119         sp_dcc_save_profile(NULL, tbl);
4120         return;
4121     }
4123     if (g_object_get_data(tbl, "presets_blocked")) 
4124         return;
4126     gchar *const preset_path = get_pref_nth_child("tools.calligraphic.preset", preset_index);
4128     if (preset_path) {
4129         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
4131         Inkscape::XML::Node *preset_repr = inkscape_get_repr(INKSCAPE, preset_path);
4133         for ( Inkscape::Util::List<Inkscape::XML::AttributeRecord const> iter = preset_repr->attributeList();
4134               iter; 
4135               ++iter ) {
4136             const gchar *attr_name = g_quark_to_string(iter->key);
4137             if (!strcmp(attr_name, "id") || !strcmp(attr_name, "name"))
4138                 continue;
4139             void *widget = g_object_get_data(tbl, attr_name);
4140             if (widget) {
4141                 if (GTK_IS_ADJUSTMENT(widget)) {
4142                     double v = prefs_get_double_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4143                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4144                     gtk_adjustment_set_value(adj, v);
4145                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4146                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4147                     int v = prefs_get_int_attribute(preset_path, attr_name, 0); // fixme: no min/max checks here, add?
4148                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4149                     gtk_toggle_action_set_active(toggle, v);
4150                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4151                 } else {
4152                     g_warning("Unknown widget type for preset: %s\n", attr_name);
4153                 }
4154             } else {
4155                 g_warning("Bad key found in a preset record: %s\n", attr_name);
4156             }
4157         }
4158         g_free(preset_path);
4159         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4160     }
4165 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4167     {
4168         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4170         EgeAdjustmentAction* calligraphy_angle = 0;
4172         {
4173         /* Width */
4174         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4175         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4176         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4177                                                               _("Pen Width"), _("Width:"),
4178                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4179                                                               "tools.calligraphic", "width", 15,
4180                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4181                                                               1, 100, 1.0, 0.0,
4182                                                               labels, values, G_N_ELEMENTS(labels),
4183                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4184         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4185         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4186         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4187         }
4189         {
4190         /* Thinning */
4191             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4192             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4193         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4194                                                               _("Stroke Thinning"), _("Thinning:"),
4195                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4196                                                               "tools.calligraphic", "thinning", 10,
4197                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4198                                                               -100, 100, 1, 0.1,
4199                                                               labels, values, G_N_ELEMENTS(labels),
4200                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4201         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4202         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4203         }
4205         {
4206         /* Angle */
4207         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4208         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4209         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4210                                                               _("Pen Angle"), _("Angle:"),
4211                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4212                                                               "tools.calligraphic", "angle", 30,
4213                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4214                                                               -90.0, 90.0, 1.0, 10.0,
4215                                                               labels, values, G_N_ELEMENTS(labels),
4216                                                               sp_ddc_angle_value_changed, 1, 0 );
4217         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4218         g_object_set_data( holder, "angle_action", eact );
4219         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4220         calligraphy_angle = eact;
4221         }
4223         {
4224         /* Fixation */
4225             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4226         gdouble values[] = {0, 20, 40, 60, 90, 100};
4227         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4228                                                               _("Fixation"), _("Fixation:"),
4229                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 1 = fixed angle)"),
4230                                                               "tools.calligraphic", "flatness", 90,
4231                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4232                                                               0.0, 100, 1.0, 10.0,
4233                                                               labels, values, G_N_ELEMENTS(labels),
4234                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4235         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4236         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4237         }
4239         {
4240         /* Cap Rounding */
4241             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4242         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4243         // TRANSLATORS: "cap" means "end" (both start and finish) here
4244         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4245                                                               _("Cap rounding"), _("Caps:"),
4246                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4247                                                               "tools.calligraphic", "cap_rounding", 0.0,
4248                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4249                                                               0.0, 5.0, 0.01, 0.1,
4250                                                               labels, values, G_N_ELEMENTS(labels),
4251                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4252         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4253         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4254         }
4256         {
4257         /* Tremor */
4258             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4259         gdouble values[] = {0, 10, 20, 40, 60, 100};
4260         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4261                                                               _("Stroke Tremor"), _("Tremor:"),
4262                                                               _("Increase to make strokes rugged and trembling"),
4263                                                               "tools.calligraphic", "tremor", 0.0,
4264                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4265                                                               0.0, 100, 1, 0.0,
4266                                                               labels, values, G_N_ELEMENTS(labels),
4267                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4269         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4270         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4271         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4272         }
4274         {
4275         /* Wiggle */
4276         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4277         gdouble values[] = {0, 20, 40, 60, 100};
4278         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4279                                                               _("Pen Wiggle"), _("Wiggle:"),
4280                                                               _("Increase to make the pen waver and wiggle"),
4281                                                               "tools.calligraphic", "wiggle", 0.0,
4282                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4283                                                               0.0, 100, 1, 0.0,
4284                                                               labels, values, G_N_ELEMENTS(labels),
4285                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4286         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4287         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4288         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4289         }
4291         {
4292         /* Mass */
4293             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4294         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4295         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4296                                                               _("Pen Mass"), _("Mass:"),
4297                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4298                                                               "tools.calligraphic", "mass", 2.0,
4299                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4300                                                               0.0, 100, 1, 0.0,
4301                                                               labels, values, G_N_ELEMENTS(labels),
4302                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4303         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4304         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4305         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4306         }
4309         /* Trace Background button */
4310         {
4311             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4312                                                           _("Trace Background"),
4313                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4314                                                           "trace_background",
4315                                                           Inkscape::ICON_SIZE_DECORATION );
4316             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4317             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4318             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "tracebackground", 0 ) );
4319             g_object_set_data( holder, "tracebackground", act );
4320         }
4322         /* Use Pressure button */
4323         {
4324             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4325                                                           _("Pressure"),
4326                                                           _("Use the pressure of the input device to alter the width of the pen"),
4327                                                           "use_pressure",
4328                                                           Inkscape::ICON_SIZE_DECORATION );
4329             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4330             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4331             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usepressure", 1 ) );
4332             g_object_set_data( holder, "usepressure", act );
4333         }
4335         /* Use Tilt button */
4336         {
4337             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4338                                                           _("Tilt"),
4339                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4340                                                           "use_tilt",
4341                                                           Inkscape::ICON_SIZE_DECORATION );
4342             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4343             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4344             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4345             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.calligraphic", "usetilt", 1 ) );
4346             g_object_set_data( holder, "usetilt", act );
4347         }
4349         /*calligraphic profile */
4350         {
4351             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4352             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4353             ege_select_one_action_set_appearance (act1, "compact");
4354             g_object_set_data (holder, "profile_selector", act1 );
4356             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4358             sp_dcc_build_presets_list (holder);
4360             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4361             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4362         }
4363     }
4367 //########################
4368 //##    Circle / Arc    ##
4369 //########################
4371 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4373     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4374     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4376     if (v1 == 0 && v2 == 0) {
4377         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4378             gtk_action_set_sensitive( ocb, FALSE );
4379             gtk_action_set_sensitive( make_whole, FALSE );
4380         }
4381     } else {
4382         gtk_action_set_sensitive( ocb, TRUE );
4383         gtk_action_set_sensitive( make_whole, TRUE );
4384     }
4387 static void
4388 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4390     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4392     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4393         prefs_set_double_attribute("tools.shapes.arc", value_name, (adj->value * M_PI)/ 180);
4394     }
4396     // quit if run by the attr_changed listener
4397     if (g_object_get_data( tbl, "freeze" )) {
4398         return;
4399     }
4401     // in turn, prevent listener from responding
4402     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4404     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4406     bool modmade = false;
4407     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4408          items != NULL;
4409          items = items->next)
4410     {
4411         SPItem *item = SP_ITEM(items->data);
4413         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4415             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4416             SPArc *arc = SP_ARC(item);
4418             if (!strcmp(value_name, "start"))
4419                 ge->start = (adj->value * M_PI)/ 180;
4420             else
4421                 ge->end = (adj->value * M_PI)/ 180;
4423             sp_genericellipse_normalize(ge);
4424             ((SPObject *)arc)->updateRepr();
4425             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4427             modmade = true;
4428         }
4429     }
4431     g_free(namespaced_name);
4433     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4435     sp_arctb_sensitivize( tbl, adj->value, other->value );
4437     if (modmade) {
4438         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4439                                    _("Arc: Change start/end"));
4440     }
4442     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4446 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4448     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4451 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4453     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4457 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4459     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4460     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4461         if ( ege_select_one_action_get_active( act ) != 0 ) {
4462             prefs_set_string_attribute("tools.shapes.arc", "open", "true");
4463         } else {
4464             prefs_set_string_attribute("tools.shapes.arc", "open", NULL);
4465         }
4466     }
4468     // quit if run by the attr_changed listener
4469     if (g_object_get_data( tbl, "freeze" )) {
4470         return;
4471     }
4473     // in turn, prevent listener from responding
4474     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4476     bool modmade = false;
4478     if ( ege_select_one_action_get_active(act) != 0 ) {
4479         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4480              items != NULL;
4481              items = items->next)
4482         {
4483             if (SP_IS_ARC((SPItem *) items->data)) {
4484                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4485                 repr->setAttribute("sodipodi:open", "true");
4486                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4487                 modmade = true;
4488             }
4489         }
4490     } else {
4491         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4492              items != NULL;
4493              items = items->next)
4494         {
4495             if (SP_IS_ARC((SPItem *) items->data))    {
4496                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
4497                 repr->setAttribute("sodipodi:open", NULL);
4498                 SP_OBJECT((SPItem *) items->data)->updateRepr();
4499                 modmade = true;
4500             }
4501         }
4502     }
4504     if (modmade) {
4505         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
4506                                    _("Arc: Change open/closed"));
4507     }
4509     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4512 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
4514     GtkAdjustment *adj;
4515     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
4516     gtk_adjustment_set_value(adj, 0.0);
4517     gtk_adjustment_value_changed(adj);
4519     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
4520     gtk_adjustment_set_value(adj, 0.0);
4521     gtk_adjustment_value_changed(adj);
4523     spinbutton_defocus( GTK_OBJECT(obj) );
4526 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
4527                                       gchar const */*old_value*/, gchar const */*new_value*/,
4528                                       bool /*is_interactive*/, gpointer data)
4530     GObject *tbl = G_OBJECT(data);
4532     // quit if run by the _changed callbacks
4533     if (g_object_get_data( tbl, "freeze" )) {
4534         return;
4535     }
4537     // in turn, prevent callbacks from responding
4538     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4540     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
4541     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
4543     GtkAdjustment *adj1,*adj2;
4544     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
4545     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
4546     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
4547     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
4549     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
4551     char const *openstr = NULL;
4552     openstr = repr->attribute("sodipodi:open");
4553     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
4555     if (openstr) {
4556         ege_select_one_action_set_active( ocb, 1 );
4557     } else {
4558         ege_select_one_action_set_active( ocb, 0 );
4559     }
4561     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4564 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
4565     NULL, /* child_added */
4566     NULL, /* child_removed */
4567     arc_tb_event_attr_changed,
4568     NULL, /* content_changed */
4569     NULL  /* order_changed */
4570 };
4573 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
4575     int n_selected = 0;
4576     Inkscape::XML::Node *repr = NULL;
4578     purge_repr_listener( tbl, tbl );
4580     for (GSList const *items = selection->itemList();
4581          items != NULL;
4582          items = items->next)
4583     {
4584         if (SP_IS_ARC((SPItem *) items->data)) {
4585             n_selected++;
4586             repr = SP_OBJECT_REPR((SPItem *) items->data);
4587         }
4588     }
4590     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
4592     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
4593     if (n_selected == 0) {
4594         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
4595     } else if (n_selected == 1) {
4596         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
4597         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4599         if (repr) {
4600             g_object_set_data( tbl, "repr", repr );
4601             Inkscape::GC::anchor(repr);
4602             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
4603             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
4604         }
4605     } else {
4606         // FIXME: implement averaging of all parameters for multiple selected
4607         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
4608         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
4609         sp_arctb_sensitivize( tbl, 1, 0 );
4610     }
4614 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4616     EgeAdjustmentAction* eact = 0;
4617     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
4620     {
4621         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
4622         ege_output_action_set_use_markup( act, TRUE );
4623         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4624         g_object_set_data( holder, "mode_action", act );
4625     }
4627     /* Start */
4628     {
4629         eact = create_adjustment_action( "ArcStartAction",
4630                                          _("Start"), _("Start:"),
4631                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
4632                                          "tools.shapes.arc", "start", 0.0,
4633                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
4634                                          -360.0, 360.0, 1.0, 10.0,
4635                                          0, 0, 0,
4636                                          sp_arctb_start_value_changed);
4637         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4638     }
4640     /* End */
4641     {
4642         eact = create_adjustment_action( "ArcEndAction",
4643                                          _("End"), _("End:"),
4644                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
4645                                          "tools.shapes.arc", "end", 0.0,
4646                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
4647                                          -360.0, 360.0, 1.0, 10.0,
4648                                          0, 0, 0,
4649                                          sp_arctb_end_value_changed);
4650         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4651     }
4653     /* Segments / Pie checkbox */
4654     {
4655         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4657         GtkTreeIter iter;
4658         gtk_list_store_append( model, &iter );
4659         gtk_list_store_set( model, &iter,
4660                             0, _("Closed arc"),
4661                             1, _("Switch to segment (closed shape with two radii)"),
4662                             2, "circle_closed_arc",
4663                             -1 );
4665         gtk_list_store_append( model, &iter );
4666         gtk_list_store_set( model, &iter,
4667                             0, _("Open Arc"),
4668                             1, _("Switch to arc (unclosed shape)"),
4669                             2, "circle_open_arc",
4670                             -1 );
4672         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
4673         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4674         g_object_set_data( holder, "open_action", act );
4676         ege_select_one_action_set_appearance( act, "full" );
4677         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4678         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4679         ege_select_one_action_set_icon_column( act, 2 );
4680         ege_select_one_action_set_icon_size( act, secondarySize );
4681         ege_select_one_action_set_tooltip_column( act, 1  );
4683         gchar const *openstr = prefs_get_string_attribute("tools.shapes.arc", "open");
4684         bool isClosed = (!openstr || (openstr && !strcmp(openstr, "false")));
4685         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
4686         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
4687     }
4689     /* Make Whole */
4690     {
4691         InkAction* inky = ink_action_new( "ArcResetAction",
4692                                           _("Make whole"),
4693                                           _("Make the shape a whole ellipse, not arc or segment"),
4694                                           "reset_circle",
4695                                           secondarySize );
4696         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
4697         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4698         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
4699         g_object_set_data( holder, "make_whole", inky );
4700     }
4702     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
4703     // sensitivize make whole and open checkbox
4704     {
4705         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
4706         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
4707         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
4708     }
4711     sigc::connection *connection = new sigc::connection(
4712         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
4713         );
4714     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
4715     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4721 // toggle button callbacks and updaters
4723 //########################
4724 //##      Dropper       ##
4725 //########################
4727 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
4728     prefs_set_int_attribute( "tools.dropper", "pick", gtk_toggle_action_get_active( act ) );
4729     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
4730     if ( set_action ) {
4731         if ( gtk_toggle_action_get_active( act ) ) {
4732             gtk_action_set_sensitive( set_action, TRUE );
4733         } else {
4734             gtk_action_set_sensitive( set_action, FALSE );
4735         }
4736     }
4738     spinbutton_defocus(GTK_OBJECT(tbl));
4741 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
4742     prefs_set_int_attribute( "tools.dropper", "setalpha", gtk_toggle_action_get_active( act ) ? 1 : 0 );
4743     spinbutton_defocus(GTK_OBJECT(tbl));
4747 /**
4748  * Dropper auxiliary toolbar construction and setup.
4749  *
4750  * TODO: Would like to add swatch of current color.
4751  * TODO: Add queue of last 5 or so colors selected with new swatches so that
4752  *       can drag and drop places. Will provide a nice mixing palette.
4753  */
4754 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4756     gint pickAlpha = prefs_get_int_attribute( "tools.dropper", "pick", 1 );
4758     {
4759         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
4760         ege_output_action_set_use_markup( act, TRUE );
4761         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4762     }
4764     {
4765         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
4766                                                       _("Pick opacity"),
4767                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
4768                                                       NULL,
4769                                                       Inkscape::ICON_SIZE_DECORATION );
4770         g_object_set( act, "short_label", _("Pick"), NULL );
4771         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4772         g_object_set_data( holder, "pick_action", act );
4773         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
4774         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
4775     }
4777     {
4778         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
4779                                                       _("Assign opacity"),
4780                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
4781                                                       NULL,
4782                                                       Inkscape::ICON_SIZE_DECORATION );
4783         g_object_set( act, "short_label", _("Assign"), NULL );
4784         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4785         g_object_set_data( holder, "set_action", act );
4786         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.dropper", "setalpha", 1 ) );
4787         // make sure it's disabled if we're not picking alpha
4788         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
4789         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
4790     }
4794 //########################
4795 //##      LPETool       ##
4796 //########################
4798 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
4800 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
4801 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
4803     using namespace Inkscape::LivePathEffect;
4805     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
4806     SPEventContext *ec = desktop->event_context;
4807     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
4808         return;
4809     }
4811     // only take action if run by the attr_changed listener
4812     if (!g_object_get_data(tbl, "freeze")) {
4813         // in turn, prevent listener from responding
4814         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4816         gint mode = ege_select_one_action_get_active(act);
4817         EffectType type = lpesubtools[mode];
4819         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4820         bool success = lpetool_try_construction(lc, type);
4821         if (success) {
4822             // since the construction was already performed, we set the state back to inactive
4823             ege_select_one_action_set_active(act, 0);
4824             mode = 0;
4825         } else {
4826             // switch to the chosen subtool
4827             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
4828         }
4830         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4831             prefs_set_int_attribute( "tools.lpetool", "mode", mode );
4832         }
4834         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
4835     }
4838 void
4839 sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
4841     SPEventContext *ec = selection->desktop()->event_context;
4842     if (!SP_IS_LPETOOL_CONTEXT(ec))
4843         return;
4845     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
4848 void
4849 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
4851     using namespace Inkscape::LivePathEffect;
4852     SPEventContext *ec = selection->desktop()->event_context;
4853     if (!SP_IS_LPETOOL_CONTEXT(ec))
4854         return;
4855     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
4857     lpetool_delete_measuring_items(lc);
4858     lpetool_create_measuring_items(lc, selection);
4860     // activate line segment combo box if a single item with LPELineSegment is selected
4861     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
4862     SPItem *item = selection->singleItem();
4863     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
4864         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
4865         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
4866         if (lpe && lpe->effectType() == LINE_SEGMENT) {
4867             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
4868             g_object_set_data(tbl, "currentlpe", lpe);
4869             g_object_set_data(tbl, "currentlpeitem", lpeitem);
4870             gtk_action_set_sensitive(w, TRUE);
4871             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
4872         } else {
4873             g_object_set_data(tbl, "currentlpe", NULL);
4874             g_object_set_data(tbl, "currentlpeitem", NULL);
4875             gtk_action_set_sensitive(w, FALSE);
4876         }
4877     } else {
4878         g_object_set_data(tbl, "currentlpe", NULL);
4879         g_object_set_data(tbl, "currentlpeitem", NULL);
4880         gtk_action_set_sensitive(w, FALSE);
4881     }
4884 static void
4885 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
4886     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4888     bool show = gtk_toggle_action_get_active( act );
4889     prefs_set_int_attribute ("tools.lpetool", "show_bbox",  show ? 1 : 0);
4891     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4892         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4893         lpetool_context_reset_limiting_bbox(lc);
4894     }
4897 static void
4898 lpetool_toggle_show_measuring_info (GtkToggleAction *act, gpointer data) {
4899     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4900     if (!tools_isactive(desktop, TOOLS_LPETOOL))
4901         return;
4903     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4904     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
4905         bool show = gtk_toggle_action_get_active( act );
4906         prefs_set_int_attribute ("tools.lpetool", "show_measuring_info",  show ? 1 : 0);
4907         lpetool_show_measuring_info(lc, show);
4908     }
4911 static void
4912 lpetool_unit_changed(GtkAction* act, GObject* tbl) {
4913     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
4914     SPUnit const *unit = tracker->getActiveUnit();
4915     prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
4917     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4918     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
4919         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
4920         lpetool_delete_measuring_items(lc);
4921         lpetool_create_measuring_items(lc);
4922     }
4925 static void
4926 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
4927     SPDesktop *desktop = static_cast<SPDesktop *>(data);
4928     Inkscape::Selection *selection = desktop->selection;
4930     boost::optional<NR::Rect> bbox = selection->bounds();
4932     if (bbox) {
4933         Geom::Point A(bbox->min());
4934         Geom::Point B(bbox->max());
4936         A *= desktop->doc2dt();
4937         B *= desktop->doc2dt();
4939         // TODO: should we provide a way to store points in prefs?
4940         prefs_set_double_attribute ("tools.lpetool", "bbox_upperleftx", A[Geom::X]);
4941         prefs_set_double_attribute ("tools.lpetool", "bbox_upperlefty", A[Geom::Y]);
4942         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrightx", B[Geom::X]);
4943         prefs_set_double_attribute ("tools.lpetool", "bbox_lowerrighty", B[Geom::Y]);
4945         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
4946     }
4948     gtk_toggle_action_set_active(act, false);
4951 static void
4952 sp_line_segment_build_list(GObject *tbl) 
4954     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
4956     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
4957     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4958     gtk_list_store_clear (model);
4960     // TODO: we add the entries of rht combo box manually; later this should be done automatically
4961     {
4962         GtkTreeIter iter;
4963         gtk_list_store_append( model, &iter );
4964         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
4965         gtk_list_store_append( model, &iter );
4966         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
4967         gtk_list_store_append( model, &iter );
4968         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
4969         gtk_list_store_append( model, &iter );
4970         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
4971     }
4973     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
4976 static void
4977 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
4978     using namespace Inkscape::LivePathEffect;
4980     // quit if run by the attr_changed listener
4981     if (g_object_get_data(tbl, "freeze")) {
4982         return;
4983     }
4985     // in turn, prevent listener from responding
4986     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
4988     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
4989     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4990     if (lpeitem) {
4991         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
4992         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
4993         sp_lpe_item_update_patheffect(lpeitem, true, true);
4994     }
4996     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4999 static void
5000 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5001     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5003     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5004         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5005         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5006     }
5007     gtk_toggle_action_set_active(act, false);
5010 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5012     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5013     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5014     g_object_set_data(holder, "tracker", tracker);
5015     SPUnit const *unit = tracker->getActiveUnit();
5016     prefs_set_int_attribute("tools.lpetool", "unitid", unit->unit_id);
5018     /** Automatically create a list of LPEs that get added to the toolbar **/
5019     {
5020         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5022         GtkTreeIter iter;
5024         // the first toggle button represents the state that no subtool is active (remove this when
5025         // this can be modeled by EgeSelectOneAction or some other action)
5026         gtk_list_store_append( model, &iter );
5027         gtk_list_store_set( model, &iter,
5028                             0, _("All inactive"),
5029                             1, _("No geometric tool is active"),
5030                             2, _("all_inactive"),
5031                             -1 );
5033         Inkscape::LivePathEffect::EffectType type;
5034         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5035             type =  lpesubtools[i];
5036             gtk_list_store_append( model, &iter );
5037             gtk_list_store_set( model, &iter,
5038                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5039                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5040                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5041                                 -1 );
5042         }
5044         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5045         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5046         g_object_set_data( holder, "lpetool_mode_action", act );
5048         ege_select_one_action_set_appearance( act, "full" );
5049         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5050         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5051         ege_select_one_action_set_icon_column( act, 2 );
5052         ege_select_one_action_set_tooltip_column( act, 1  );
5054         gint lpeToolMode = prefs_get_int_attribute("tools.lpetool", "mode", 0);
5055         ege_select_one_action_set_active( act, lpeToolMode );
5056         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5057     }
5059     /* Show limiting bounding box */
5060     {
5061         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5062                                                       _("Show limiting bounding box"),
5063                                                       _("Show bounding box (used to cut infinite lines)"),
5064                                                       "lpetool_show_bbox",
5065                                                       Inkscape::ICON_SIZE_DECORATION );
5066         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5067         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5068         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_bbox", 1 ) );
5069     }
5071     /* Set limiting bounding box to bbox of current selection */
5072     {
5073         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5074                                                       _("Get limiting bounding box from selection"),
5075                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5076                                                       "lpetool_set_bbox",
5077                                                       Inkscape::ICON_SIZE_DECORATION );
5078         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5079         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5080         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5081     }
5084     /* Combo box to choose line segment type */
5085     {
5086         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5087         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5088         ege_select_one_action_set_appearance (act, "compact");
5089         g_object_set_data (holder, "lpetool_line_segment_action", act );
5091         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5093         sp_line_segment_build_list (holder);
5095         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5096         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5097         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5098     }
5100     /* Display measuring info for selected items */
5101     {
5102         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5103                                                       _("Display measuring info"),
5104                                                       _("Display measuring info for selected items"),
5105                                                       "lpetool_measuring_info",
5106                                                       Inkscape::ICON_SIZE_DECORATION );
5107         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5108         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), desktop );
5109         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.lpetool", "show_measuring_info", 1 ) );
5110     }
5112     // add the units menu
5113     {
5114         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5115         gtk_action_group_add_action( mainActions, act );
5116         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5117     }
5119     /* Open LPE dialog (to adapt parameters numerically) */
5120     {
5121         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5122                                                       _("Open LPE dialog"),
5123                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5124                                                       "lpetool_open_lpe_dialog",
5125                                                       Inkscape::ICON_SIZE_DECORATION );
5126         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5127         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5128         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5129     }
5131     //watch selection
5132     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5134     sigc::connection *c_selection_modified =
5135         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5136                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5137     pool->add_connection ("selection-modified", c_selection_modified);
5139     sigc::connection *c_selection_changed =
5140         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5141                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5142     pool->add_connection ("selection-changed", c_selection_changed);
5145 //########################
5146 //##       Eraser       ##
5147 //########################
5149 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5151     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5152     gint eraserMode = (ege_select_one_action_get_active( act ) != 0) ? 1 : 0;
5153     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5154         prefs_set_int_attribute( "tools.eraser", "mode", eraserMode );
5155     }
5157     // only take action if run by the attr_changed listener
5158     if (!g_object_get_data( tbl, "freeze" )) {
5159         // in turn, prevent listener from responding
5160         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5162         if ( eraserMode != 0 ) {
5163         } else {
5164         }
5165         // TODO finish implementation
5167         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5168     }
5171 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5173     {
5174         /* Width */
5175         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5176         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5177         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5178                                                               _("Pen Width"), _("Width:"),
5179                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5180                                                               "tools.eraser", "width", 15,
5181                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5182                                                               1, 100, 1.0, 0.0,
5183                                                               labels, values, G_N_ELEMENTS(labels),
5184                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
5185         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5186         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5187         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5188     }
5190     {
5191         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5193         GtkTreeIter iter;
5194         gtk_list_store_append( model, &iter );
5195         gtk_list_store_set( model, &iter,
5196                             0, _("Delete"),
5197                             1, _("Delete objects touched by the eraser"),
5198                             2, "delete_object",
5199                             -1 );
5201         gtk_list_store_append( model, &iter );
5202         gtk_list_store_set( model, &iter,
5203                             0, _("Cut"),
5204                             1, _("Cut out from objects"),
5205                             2, "difference",
5206                             -1 );
5208         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5209         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5210         g_object_set_data( holder, "eraser_mode_action", act );
5212         ege_select_one_action_set_appearance( act, "full" );
5213         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5214         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5215         ege_select_one_action_set_icon_column( act, 2 );
5216         ege_select_one_action_set_tooltip_column( act, 1  );
5218         gint eraserMode = (prefs_get_int_attribute("tools.eraser", "mode", 0) != 0) ? 1 : 0;
5219         ege_select_one_action_set_active( act, eraserMode );
5220         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5221     }
5225 //########################
5226 //##    Text Toolbox    ##
5227 //########################
5228 /*
5229 static void
5230 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5232     //Call back for letter sizing spinbutton
5235 static void
5236 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5238     //Call back for line height spinbutton
5241 static void
5242 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5244     //Call back for horizontal kerning spinbutton
5247 static void
5248 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5250     //Call back for vertical kerning spinbutton
5253 static void
5254 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5256     //Call back for letter rotation spinbutton
5257 }*/
5259 namespace {
5261 bool popdown_visible = false;
5262 bool popdown_hasfocus = false;
5264 void
5265 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5267     SPStyle *query =
5268         sp_style_new (SP_ACTIVE_DOCUMENT);
5270 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5272     int result_family =
5273         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5275     int result_style =
5276         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5278     int result_numbers =
5279         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5281     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5283     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5284     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5285         // there are no texts in selection, read from prefs
5286  
5287         Inkscape::XML::Node *repr = inkscape_get_repr (INKSCAPE, "tools.text");
5288         if (repr) {
5289             sp_style_read_from_repr (query, repr);
5290             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5291                 // do not reset the toolbar style from prefs if we already did it last time
5292                 sp_style_unref(query);
5293                 return;
5294             }
5295             g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5296         } else {
5297             sp_style_unref(query);
5298             return;
5299         }
5300     } else {
5301         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5302     }
5304     if (query->text)
5305     {
5306         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5307             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5308             gtk_entry_set_text (GTK_ENTRY (entry), "");
5310         } else if (query->text->font_specification.value || query->text->font_family.value) {
5312             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5314             // Get the font that corresponds
5315             Glib::ustring familyName;
5317             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5318             if (font) {
5319                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5320                 font->Unref();
5321                 font = NULL;
5322             }
5324             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5326             Gtk::TreePath path;
5327             try {
5328                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5329             } catch (...) {
5330                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5331                 sp_style_unref(query);
5332                 return;
5333             }
5335             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5336             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5338             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5340             gtk_tree_selection_select_path (tselection, path.gobj());
5341             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5343             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5344         }
5346         //Size
5347         {
5348             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5349             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5350             g_object_set_data(tbl, "size-block", gpointer(1));
5351             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5352             g_object_set_data(tbl, "size-block", gpointer(0));
5353             g_free(str);
5354         }
5356         //Anchor
5357         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5358         {
5359             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5360             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5361             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5362             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5363         }
5364         else
5365         {
5366             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5367             {
5368                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5369                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5370                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5371                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5372             }
5373             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5374             {
5375                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5376                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5377                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5378                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5379             }
5380             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5381             {
5382                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5383                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5384                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5385                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5386             }
5387         }
5389         //Style
5390         {
5391             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5393             gboolean active = gtk_toggle_button_get_active (button);
5394             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5396             if (active != check)
5397             {
5398                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5399                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5400                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5401             }
5402         }
5404         {
5405             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5407             gboolean active = gtk_toggle_button_get_active (button);
5408             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5410             if (active != check)
5411             {
5412                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5413                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5414                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5415             }
5416         }
5418         //Orientation
5419         //locking both buttons, changing one affect all group (both)
5420         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5421         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5423         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5424         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5426         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5427         {
5428             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5429         }
5430         else
5431         {
5432             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5433         }
5434         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5435         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5436     }
5438     sp_style_unref(query);
5441 void
5442 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5444     sp_text_toolbox_selection_changed (selection, tbl);
5447 void
5448 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5450     sp_text_toolbox_selection_changed (NULL, tbl);
5453 void
5454 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5455                                 GObject             *tbl)
5457     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
5458     GtkTreeModel *model = 0;
5459     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5460     GtkTreeIter   iter;
5461     char         *family = 0;
5463     gdk_pointer_ungrab (GDK_CURRENT_TIME);
5464     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5466     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
5467         return;
5468     }
5470     gtk_tree_model_get (model, &iter, 0, &family, -1);
5472     if (g_object_get_data (G_OBJECT (selection), "block"))
5473     {
5474         gtk_entry_set_text (GTK_ENTRY (entry), family);
5475         return;
5476     }
5478     gtk_entry_set_text (GTK_ENTRY (entry), family);
5480     SPStyle *query =
5481         sp_style_new (SP_ACTIVE_DOCUMENT);
5483     int result_fontspec =
5484         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5486     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5488     SPCSSAttr *css = sp_repr_css_attr_new ();
5491     // First try to get the font spec from the stored value
5492     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5494     if (fontSpec.empty()) {
5495         // Construct a new font specification if it does not yet exist
5496         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5497         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5498         fontFromStyle->Unref();
5499     }
5501     if (!fontSpec.empty()) {
5502         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
5503         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
5504             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
5505             if (font) {
5506                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5508                 // Set all the these just in case they were altered when finding the best
5509                 // match for the new family and old style...
5511                 gchar c[256];
5513                 font->Family(c, 256);
5514                 sp_repr_css_set_property (css, "font-family", c);
5516                 font->Attribute( "weight", c, 256);
5517                 sp_repr_css_set_property (css, "font-weight", c);
5519                 font->Attribute("style", c, 256);
5520                 sp_repr_css_set_property (css, "font-style", c);
5522                 font->Attribute("stretch", c, 256);
5523                 sp_repr_css_set_property (css, "font-stretch", c);
5525                 font->Attribute("variant", c, 256);
5526                 sp_repr_css_set_property (css, "font-variant", c);
5528                 font->Unref();
5529             }
5530         }
5531     }
5533     // If querying returned nothing, set the default style of the tool (for new texts)
5534     if (result_fontspec == QUERY_STYLE_NOTHING)
5535     {
5536         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5537         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
5538     }
5539     else
5540     {
5541         sp_desktop_set_style (desktop, css, true, true);
5542     }
5544     sp_style_unref(query);
5546     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5547                                    _("Text: Change font family"));
5548     sp_repr_css_attr_unref (css);
5549     g_free(family);
5550     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5552     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5555 /* This is where execution comes when the contents of the font family box have been completed
5556    by the press of the return key */
5557 void
5558 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
5559                                        GObject      *tbl)
5561     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
5563 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
5564     try {
5565         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
5566         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5567         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5568         gtk_tree_selection_select_path (selection, path.gobj());
5569         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5570         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5571     } catch (...) {
5572         if (family && strlen (family))
5573         {
5574             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5575         }
5576     }
5579 void
5580 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
5581                                    gpointer          data)
5583     if (g_object_get_data (G_OBJECT (button), "block")) return;
5584     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
5585     int prop = GPOINTER_TO_INT(data);
5587     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5588     SPCSSAttr *css = sp_repr_css_attr_new ();
5590     switch (prop)
5591     {
5592         case 0:
5593         {
5594             sp_repr_css_set_property (css, "text-anchor", "start");
5595             sp_repr_css_set_property (css, "text-align", "start");
5596             break;
5597         }
5598         case 1:
5599         {
5600             sp_repr_css_set_property (css, "text-anchor", "middle");
5601             sp_repr_css_set_property (css, "text-align", "center");
5602             break;
5603         }
5605         case 2:
5606         {
5607             sp_repr_css_set_property (css, "text-anchor", "end");
5608             sp_repr_css_set_property (css, "text-align", "end");
5609             break;
5610         }
5612         case 3:
5613         {
5614             sp_repr_css_set_property (css, "text-anchor", "start");
5615             sp_repr_css_set_property (css, "text-align", "justify");
5616             break;
5617         }
5618     }
5620     SPStyle *query =
5621         sp_style_new (SP_ACTIVE_DOCUMENT);
5622     int result_numbers =
5623         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5625     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5626     if (result_numbers == QUERY_STYLE_NOTHING)
5627     {
5628         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5629     }
5631     sp_style_unref(query);
5633     sp_desktop_set_style (desktop, css, true, true);
5634     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5635                                    _("Text: Change alignment"));
5636     sp_repr_css_attr_unref (css);
5638     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5641 void
5642 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
5643                                gpointer          data)
5645     if (g_object_get_data (G_OBJECT (button), "block")) return;
5647     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5648     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5649     int          prop       = GPOINTER_TO_INT(data);
5650     bool         active     = gtk_toggle_button_get_active (button);
5652     SPStyle *query =
5653         sp_style_new (SP_ACTIVE_DOCUMENT);
5655     int result_fontspec =
5656         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5658     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5659     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5660     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5662     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
5663     Glib::ustring newFontSpec = "";
5665     if (fontSpec.empty()) {
5666         // Construct a new font specification if it does not yet exist
5667         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
5668         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
5669         fontFromStyle->Unref();
5670     }
5672     switch (prop)
5673     {
5674         case 0:
5675         {
5676             if (!fontSpec.empty()) {
5677                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
5678             }
5679             if (fontSpec != newFontSpec) {
5680                 // Don't even set the bold if the font didn't exist on the system
5681                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
5682             }
5683             break;
5684         }
5686         case 1:
5687         {
5688             if (!fontSpec.empty()) {
5689                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
5690             }
5691             if (fontSpec != newFontSpec) {
5692                 // Don't even set the italic if the font didn't exist on the system
5693                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
5694             }
5695             break;
5696         }
5697     }
5699     if (!newFontSpec.empty()) {
5700         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
5701     }
5703     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5704     if (result_fontspec == QUERY_STYLE_NOTHING)
5705     {
5706         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5707     }
5709     sp_style_unref(query);
5711     sp_desktop_set_style (desktop, css, true, true);
5712     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5713                                    _("Text: Change font style"));
5714     sp_repr_css_attr_unref (css);
5716     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5719 void
5720 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
5721                                      gpointer         data)
5723     if (g_object_get_data (G_OBJECT (button), "block")) {
5724         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5725         return;
5726     }
5728     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
5729     SPCSSAttr   *css        = sp_repr_css_attr_new ();
5730     int          prop       = GPOINTER_TO_INT(data);
5732     switch (prop)
5733     {
5734         case 0:
5735         {
5736             sp_repr_css_set_property (css, "writing-mode", "lr");
5737             break;
5738         }
5740         case 1:
5741         {
5742             sp_repr_css_set_property (css, "writing-mode", "tb");
5743             break;
5744         }
5745     }
5747     SPStyle *query =
5748         sp_style_new (SP_ACTIVE_DOCUMENT);
5749     int result_numbers =
5750         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5752     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5753     if (result_numbers == QUERY_STYLE_NOTHING)
5754     {
5755         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5756     }
5758     sp_desktop_set_style (desktop, css, true, true);
5759     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
5760                                    _("Text: Change orientation"));
5761     sp_repr_css_attr_unref (css);
5763     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5766 gboolean
5767 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
5769     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5770     if (!desktop) return FALSE;
5772     switch (get_group0_keyval (event)) {
5773         case GDK_Escape: // defocus
5774             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5775             sp_text_toolbox_selection_changed (NULL, tbl); // update
5776             return TRUE; // I consumed the event
5777             break;
5778     }
5779     return FALSE;
5782 gboolean
5783 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
5785     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5786     if (!desktop) return FALSE;
5788     switch (get_group0_keyval (event)) {
5789         case GDK_KP_Enter:
5790         case GDK_Return:
5791         case GDK_Escape: // defocus
5792             gtk_widget_hide (w);
5793             popdown_visible = false;
5794             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5795             return TRUE; // I consumed the event
5796             break;
5797         case GDK_w:
5798         case GDK_W:
5799             if (event->state & GDK_CONTROL_MASK) {
5800                 gtk_widget_hide (w);
5801                 popdown_visible = false;
5802                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5803                 return TRUE; // I consumed the event
5804             }
5805             break;
5806     }
5807     return FALSE;
5811 void
5812 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
5813                                GObject     *tbl)
5815     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5817     if (g_object_get_data (tbl, "size-block")) return;
5819     // If this is not from selecting a size in the list (in which case get_active will give the
5820     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
5821     // process this event. This fixes GTK's stupid insistence on sending an activate change every
5822     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
5823     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
5824         return;
5826     gdouble value = -1;
5827     {
5828         gchar *endptr;
5829         gchar *const text = gtk_combo_box_get_active_text(cbox);
5830         if (text) {
5831             value = g_strtod(text, &endptr);
5832             if (endptr == text) {  // Conversion failed, non-numeric input.
5833                 value = -1;
5834             }
5835             g_free(text);
5836         }
5837     }
5838     if (value <= 0) {
5839         return; // could not parse value
5840     }
5842     SPCSSAttr *css = sp_repr_css_attr_new ();
5843     Inkscape::CSSOStringStream osfs;
5844     osfs << value;
5845     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
5847     SPStyle *query =
5848         sp_style_new (SP_ACTIVE_DOCUMENT);
5849     int result_numbers =
5850         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5852     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5853     if (result_numbers == QUERY_STYLE_NOTHING)
5854     {
5855         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
5856     }
5858     sp_style_unref(query);
5860     sp_desktop_set_style (desktop, css, true, true);
5861     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
5862                                    _("Text: Change font size"));
5863     sp_repr_css_attr_unref (css);
5865     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5868 gboolean
5869 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
5871     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5872     if (!desktop) return FALSE;
5874     if (!g_object_get_data (tbl, "esc-pressed")) {
5875         g_object_set_data (tbl, "enter-pressed", gpointer(1));
5876         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5877         sp_text_toolbox_size_changed (cbox, tbl);
5878         g_object_set_data (tbl, "enter-pressed", gpointer(0));
5879     }
5880     return FALSE; // I consumed the event
5884 gboolean
5885 sp_text_toolbox_size_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_Escape: // defocus
5892             g_object_set_data (tbl, "esc-pressed", gpointer(1));
5893             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5894             g_object_set_data (tbl, "esc-pressed", gpointer(0));
5895             return TRUE; // I consumed the event
5896             break;
5897         case GDK_Return: // defocus
5898         case GDK_KP_Enter:
5899             g_object_set_data (tbl, "enter-pressed", gpointer(1));
5900             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
5901             sp_text_toolbox_size_changed (cbox, tbl);
5902             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5903             g_object_set_data (tbl, "enter-pressed", gpointer(0));
5904             return TRUE; // I consumed the event
5905             break;
5906     }
5907     return FALSE;
5910 void
5911 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
5912                                          GObject            *tbl)
5914     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
5915     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
5916     int x, y;
5918     if (!popdown_visible)
5919     {
5920         gdk_window_get_origin (widget->window, &x, &y);
5921         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
5922         gtk_widget_show_all (popdown);
5923         //sp_transientize (popdown);
5925         gdk_pointer_grab (widget->window, TRUE,
5926                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
5927                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
5928                                         GDK_POINTER_MOTION_MASK),
5929                           NULL, NULL, GDK_CURRENT_TIME);
5931         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
5933         popdown_visible = true;
5934     }
5935     else
5936     {
5937         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5938         gdk_pointer_ungrab (GDK_CURRENT_TIME);
5939         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
5940         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5941         gtk_widget_hide (popdown);
5942         popdown_visible = false;
5943     }
5946 gboolean
5947 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
5948                                  GdkEventFocus    */*event*/,
5949                                  GObject          */*tbl*/)
5951     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
5952     return FALSE;
5955 gboolean
5956 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
5957                                    GdkEventFocus    */*event*/,
5958                                    GObject          */*tbl*/)
5960     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
5962     if (popdown_hasfocus) {
5963         gtk_widget_hide (popdown);
5964         popdown_hasfocus = false;
5965         popdown_visible = false;
5966         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
5967         return TRUE;
5968     }
5969     return FALSE;
5972 gboolean
5973 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
5974                                    GdkEventFocus    */*event*/,
5975                                    GObject          */*tbl*/)
5977     popdown_hasfocus = true;
5978     return TRUE;
5982 void
5983 cell_data_func  (GtkTreeViewColumn */*column*/,
5984                  GtkCellRenderer   *cell,
5985                  GtkTreeModel      *tree_model,
5986                  GtkTreeIter       *iter,
5987                  gpointer           /*data*/)
5989     gchar *family;
5990     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
5991     gchar *const family_escaped = g_markup_escape_text(family, -1);
5993     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
5994     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
5996     std::stringstream markup;
5997     markup << family_escaped << "  <span foreground='darkgray' font_family='"
5998            << family_escaped << "'>" << sample_escaped << "</span>";
5999     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6001     g_free(family);
6002     g_free(family_escaped);
6003     g_free(sample_escaped);
6006 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6007     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6008     if (completion) {
6009         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6010         g_object_unref (completion);
6011     }
6014 GtkWidget*
6015 sp_text_toolbox_new (SPDesktop *desktop)
6017     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6018     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("toolbox", "secondary", 1));
6020     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6021     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6023     GtkTooltips *tt = gtk_tooltips_new();
6024     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6026     ////////////Family
6027     //Window
6028     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6029     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6031     //Entry
6032     GtkWidget           *entry = gtk_entry_new ();
6033     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6034     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6035     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6036     gtk_entry_completion_set_text_column (completion, 0);
6037     gtk_entry_completion_set_minimum_key_length (completion, 1);
6038     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6039     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6040     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6041     gtk_toolbar_append_widget( tbl, entry, "", "" );
6042     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6044     //Button
6045     GtkWidget   *button = gtk_button_new ();
6046     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6047     gtk_toolbar_append_widget( tbl, button, "", "");
6049     //Popdown
6050     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6051     GtkWidget           *treeview = gtk_tree_view_new ();
6053     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6054     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6055     gtk_tree_view_column_pack_start (column, cell, FALSE);
6056     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6057     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6058     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6060     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6061     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6062     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6064     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6066     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6067     gtk_container_add (GTK_CONTAINER (sw), treeview);
6069     gtk_container_add (GTK_CONTAINER (window), sw);
6070     gtk_widget_set_size_request (window, 300, 450);
6072     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6073     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6074     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6076     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6078     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6079     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6080     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6082     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6083     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6085     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6086     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6087     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6088     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6089     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6091     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6092     GtkWidget *box = gtk_event_box_new ();
6093     gtk_container_add (GTK_CONTAINER (box), image);
6094     gtk_toolbar_append_widget( tbl, box, "", "");
6095     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6096     GtkTooltips *tooltips = gtk_tooltips_new ();
6097     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6098     gtk_widget_hide (GTK_WIDGET (box));
6099     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6101     ////////////Size
6102     gchar const *const sizes[] = {
6103         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6104         "16", "18", "20", "22", "24", "28",
6105         "32", "36", "40", "48", "56", "64", "72", "144"
6106     };
6108     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6109     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6110         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6111     }
6112     gtk_widget_set_size_request (cbox, 80, -1);
6113     gtk_toolbar_append_widget( tbl, cbox, "", "");
6114     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6115     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6116     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6117     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6119     ////////////Text anchor
6120     GtkWidget *group   = gtk_radio_button_new (NULL);
6121     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6122     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6124     // left
6125     GtkWidget *rbutton = group;
6126     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6127     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6128     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6130     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6131     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6132     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6133     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6135     // center
6136     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6137     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6138     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6139     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6141     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6142     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6143     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6144     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6146     // right
6147     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6148     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6149     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6150     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6152     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6153     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6154     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6155     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6157     // fill
6158     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6159     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6160     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6161     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6163     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6164     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6165     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6166     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6168     gtk_toolbar_append_widget( tbl, row, "", "");
6170     //spacer
6171     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6173     ////////////Text style
6174     row = gtk_hbox_new (FALSE, 4);
6176     // bold
6177     rbutton = gtk_toggle_button_new ();
6178     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6179     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6180     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6181     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6183     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6184     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6185     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6187     // italic
6188     rbutton = gtk_toggle_button_new ();
6189     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6190     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6191     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6192     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6194     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6195     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6196     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6198     gtk_toolbar_append_widget( tbl, row, "", "");
6200     //spacer
6201     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6203     ////////////Text orientation
6204     group   = gtk_radio_button_new (NULL);
6205     row     = gtk_hbox_new (FALSE, 4);
6206     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6208     // horizontal
6209     rbutton = group;
6210     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6211     gtk_container_add           (GTK_CONTAINER (rbutton),
6212                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_LR));
6213     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6214     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6216     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6217     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6218     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6220     // vertical
6221     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6222     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6223     gtk_container_add           (GTK_CONTAINER (rbutton),
6224                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_STOCK_WRITING_MODE_TB));
6225     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6226     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6228     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6229     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6230     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6231     gtk_toolbar_append_widget( tbl, row, "", "" );
6234     //watch selection
6235     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6237     sigc::connection *c_selection_changed =
6238         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6239                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6240     pool->add_connection ("selection-changed", c_selection_changed);
6242     sigc::connection *c_selection_modified =
6243         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6244                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6245     pool->add_connection ("selection-modified", c_selection_modified);
6247     sigc::connection *c_subselection_changed =
6248         new sigc::connection (desktop->connectToolSubselectionChanged
6249                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6250     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6252     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6255     gtk_widget_show_all( GTK_WIDGET(tbl) );
6257     return GTK_WIDGET(tbl);
6258 } // end of sp_text_toolbox_new()
6260 }//<unnamed> namespace
6263 //#########################
6264 //##      Connector      ##
6265 //#########################
6267 static void sp_connector_path_set_avoid(void)
6269     cc_selection_set_avoid(true);
6273 static void sp_connector_path_set_ignore(void)
6275     cc_selection_set_avoid(false);
6280 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6282     // quit if run by the _changed callbacks
6283     if (g_object_get_data( tbl, "freeze" )) {
6284         return;
6285     }
6287     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6288     SPDocument *doc = sp_desktop_document(desktop);
6290     if (!sp_document_get_undo_sensitive(doc))
6291     {
6292         return;
6293     }
6295     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6297     if ( repr->attribute("inkscape:connector-spacing") ) {
6298         gdouble priorValue = gtk_adjustment_get_value(adj);
6299         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6300         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6301             return;
6302         }
6303     } else if ( adj->value == defaultConnSpacing ) {
6304         return;
6305     }
6307     // in turn, prevent callbacks from responding
6308     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6310     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6311     SP_OBJECT(desktop->namedview)->updateRepr();
6313     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6314     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6315         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6316         NR::Matrix m = NR::identity();
6317         avoid_item_move(&m, item);
6318     }
6320     if (items) {
6321         g_slist_free(items);
6322     }
6324     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6325             _("Change connector spacing"));
6327     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6329     spinbutton_defocus(GTK_OBJECT(tbl));
6332 static void sp_connector_graph_layout(void)
6334     if (!SP_ACTIVE_DESKTOP) return;
6336     // hack for clones, see comment in align-and-distribute.cpp
6337     int saved_compensation = prefs_get_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6338     prefs_set_int_attribute("options.clonecompensation", "value", SP_CLONE_COMPENSATION_UNMOVED);
6340     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6342     prefs_set_int_attribute("options.clonecompensation", "value", saved_compensation);
6344     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6347 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6349     if ( gtk_toggle_action_get_active( act ) ) {
6350         prefs_set_string_attribute("tools.connector", "directedlayout",
6351                 "true");
6352     } else {
6353         prefs_set_string_attribute("tools.connector", "directedlayout",
6354                 "false");
6355     }
6358 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6360     if ( gtk_toggle_action_get_active( act ) ) {
6361         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6362                 "true");
6363     } else {
6364         prefs_set_string_attribute("tools.connector", "avoidoverlaplayout",
6365                 "false");
6366     }
6370 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6372     prefs_set_double_attribute("tools.connector", "length", adj->value);
6373     spinbutton_defocus(GTK_OBJECT(tbl));
6376 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6377                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6378                                             bool /*is_interactive*/, gpointer data)
6380     GtkWidget *tbl = GTK_WIDGET(data);
6382     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6383         return;
6384     }
6385     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6386         return;
6387     }
6389     GtkAdjustment *adj = (GtkAdjustment*)
6390             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6391     gdouble spacing = defaultConnSpacing;
6392     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6394     gtk_adjustment_set_value(adj, spacing);
6398 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6399     NULL, /* child_added */
6400     NULL, /* child_removed */
6401     connector_tb_event_attr_changed,
6402     NULL, /* content_changed */
6403     NULL  /* order_changed */
6404 };
6407 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6409     Inkscape::IconSize secondarySize = prefToSize("toolbox", "secondary", 1);
6411     {
6412         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6413                                           _("Avoid"),
6414                                           _("Make connectors avoid selected objects"),
6415                                           "connector_avoid",
6416                                           secondarySize );
6417         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6418         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6419     }
6421     {
6422         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6423                                           _("Ignore"),
6424                                           _("Make connectors ignore selected objects"),
6425                                           "connector_ignore",
6426                                           secondarySize );
6427         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6428         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6429     }
6431     EgeAdjustmentAction* eact = 0;
6433     // Spacing spinbox
6434     eact = create_adjustment_action( "ConnectorSpacingAction",
6435                                      _("Connector Spacing"), _("Spacing:"),
6436                                      _("The amount of space left around objects by auto-routing connectors"),
6437                                      "tools.connector", "spacing", defaultConnSpacing,
6438                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6439                                      0, 100, 1.0, 10.0,
6440                                      0, 0, 0,
6441                                      connector_spacing_changed, 1, 0 );
6442     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6444     // Graph (connector network) layout
6445     {
6446         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6447                                           _("Graph"),
6448                                           _("Nicely arrange selected connector network"),
6449                                           "graph_layout",
6450                                           secondarySize );
6451         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6452         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6453     }
6455     // Default connector length spinbox
6456     eact = create_adjustment_action( "ConnectorLengthAction",
6457                                      _("Connector Length"), _("Length:"),
6458                                      _("Ideal length for connectors when layout is applied"),
6459                                      "tools.connector", "length", 100,
6460                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
6461                                      10, 1000, 10.0, 100.0,
6462                                      0, 0, 0,
6463                                      connector_length_changed, 1, 0 );
6464     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6467     // Directed edges toggle button
6468     {
6469         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
6470                                                       _("Downwards"),
6471                                                       _("Make connectors with end-markers (arrows) point downwards"),
6472                                                       "directed_graph",
6473                                                       Inkscape::ICON_SIZE_DECORATION );
6474         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6476         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "directedlayout" );
6477         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6478                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6480         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
6481     }
6483     // Avoid overlaps toggle button
6484     {
6485         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
6486                                                       _("Remove overlaps"),
6487                                                       _("Do not allow overlapping shapes"),
6488                                                       "remove_overlaps",
6489                                                       Inkscape::ICON_SIZE_DECORATION );
6490         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6492         gchar const* tbuttonstate = prefs_get_string_attribute( "tools.connector", "avoidoverlaplayout" );
6493         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act),
6494                 (tbuttonstate && !strcmp(tbuttonstate, "true")) ? TRUE:FALSE );
6496         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
6497     }
6499     // Code to watch for changes to the connector-spacing attribute in
6500     // the XML.
6501     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6502     g_assert(repr != NULL);
6504     purge_repr_listener( holder, holder );
6506     if (repr) {
6507         g_object_set_data( holder, "repr", repr );
6508         Inkscape::GC::anchor(repr);
6509         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
6510         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
6511     }
6512 } // end of sp_connector_toolbox_prep()
6515 //#########################
6516 //##     Paintbucket     ##
6517 //#########################
6519 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
6521     gint channels = ege_select_one_action_get_active( act );
6522     flood_channels_set_channels( channels );
6525 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
6527     prefs_set_int_attribute("tools.paintbucket", "threshold", (gint)adj->value);
6530 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
6532     prefs_set_int_attribute("tools.paintbucket", "autogap", ege_select_one_action_get_active( act ));
6535 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
6537     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
6538     SPUnit const *unit = tracker->getActiveUnit();
6540     prefs_set_double_attribute("tools.paintbucket", "offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
6542     prefs_set_string_attribute("tools.paintbucket", "offsetunits", sp_unit_get_abbreviation(unit));
6545 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
6547     // FIXME: make defaults settable via Inkscape Options
6548     struct KeyValue {
6549         char const *key;
6550         double value;
6551     } const key_values[] = {
6552         {"threshold", 15},
6553         {"offset", 0.0}
6554     };
6556     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
6557         KeyValue const &kv = key_values[i];
6558         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
6559         if ( adj ) {
6560             gtk_adjustment_set_value(adj, kv.value);
6561         }
6562     }
6564     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
6565     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
6566     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
6567     ege_select_one_action_set_active( autogap_action, 0 );
6570 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6572     EgeAdjustmentAction* eact = 0;
6574     {
6575         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6577         GList* items = 0;
6578         gint count = 0;
6579         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
6580         {
6581             GtkTreeIter iter;
6582             gtk_list_store_append( model, &iter );
6583             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6584             count++;
6585         }
6586         g_list_free( items );
6587         items = 0;
6588         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
6589         g_object_set( act1, "short_label", _("Fill by:"), NULL );
6590         ege_select_one_action_set_appearance( act1, "compact" );
6591         ege_select_one_action_set_active( act1, prefs_get_int_attribute("tools.paintbucket", "channels", 0) );
6592         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
6593         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
6594         g_object_set_data( holder, "channels_action", act1 );
6595     }
6597     // Spacing spinbox
6598     {
6599         eact = create_adjustment_action(
6600             "ThresholdAction",
6601             _("Fill Threshold"), _("Threshold:"),
6602             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
6603             "tools.paintbucket", "threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
6604             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
6605             0, 0, 0,
6606             paintbucket_threshold_changed, 1, 0 );
6608         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6609         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6610     }
6612     // Create the units menu.
6613     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
6614     const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
6615     if (stored_unit)
6616         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
6617     g_object_set_data( holder, "tracker", tracker );
6618     {
6619         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
6620         gtk_action_group_add_action( mainActions, act );
6621     }
6623     // Offset spinbox
6624     {
6625         eact = create_adjustment_action(
6626             "OffsetAction",
6627             _("Grow/shrink by"), _("Grow/shrink by:"),
6628             _("The amount to grow (positive) or shrink (negative) the created fill path"),
6629             "tools.paintbucket", "offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
6630             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
6631             0, 0, 0,
6632             paintbucket_offset_changed, 1, 2);
6633         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
6635         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6636     }
6638     /* Auto Gap */
6639     {
6640         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
6642         GList* items = 0;
6643         gint count = 0;
6644         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
6645         {
6646             GtkTreeIter iter;
6647             gtk_list_store_append( model, &iter );
6648             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
6649             count++;
6650         }
6651         g_list_free( items );
6652         items = 0;
6653         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
6654         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
6655         ege_select_one_action_set_appearance( act2, "compact" );
6656         ege_select_one_action_set_active( act2, prefs_get_int_attribute("tools.paintbucket", "autogap", 0) );
6657         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
6658         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
6659         g_object_set_data( holder, "autogap_action", act2 );
6660     }
6662     /* Reset */
6663     {
6664         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
6665                                           _("Defaults"),
6666                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
6667                                           GTK_STOCK_CLEAR );
6668         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
6669         gtk_action_group_add_action( mainActions, act );
6670         gtk_action_set_sensitive( act, TRUE );
6671     }
6675 /*
6676   Local Variables:
6677   mode:c++
6678   c-file-style:"stroustrup"
6679   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
6680   indent-tabs-mode:nil
6681   fill-column:99
6682   End:
6683 */
6684 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :