Code

A simple layout document as to what, why and how is cppification.
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@scislac.com>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *   Tavmjong Bah <tavmjong@free.fr>
16  *
17  * Copyright (C) 2004 David Turner
18  * Copyright (C) 2003 MenTaLguY
19  * Copyright (C) 1999-2010 authors
20  * Copyright (C) 2001-2002 Ximian, Inc.
21  *
22  * Released under GNU GPL, read the file 'COPYING' for more information
23  */
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <cstring>
30 #include <string>
32 #include <gtkmm.h>
33 #include <gtk/gtk.h>
34 #include <iostream>
35 #include <sstream>
36 #include <glibmm/i18n.h>
38 #include "../box3d-context.h"
39 #include "../box3d.h"
40 #include "../conn-avoid-ref.h"
41 #include "../connection-pool.h"
42 #include "../connector-context.h"
43 #include "../desktop.h"
44 #include "../desktop-handles.h"
45 #include "../desktop-style.h"
46 #include "../dialogs/dialog-events.h"
47 #include "../dialogs/text-edit.h"
48 #include "../document-private.h"
49 #include "../ege-adjustment-action.h"
50 #include "../ege-output-action.h"
51 #include "../ege-select-one-action.h"
52 #include "../flood-context.h"
53 #include "gradient-toolbar.h"
54 #include "../graphlayout.h"
55 #include "../helper/unit-menu.h"
56 #include "../helper/units.h"
57 #include "../helper/unit-tracker.h"
58 #include "icon.h"
59 #include "../ink-action.h"
60 #include "../ink-comboboxentry-action.h"
61 #include "../inkscape.h"
62 #include "../interface.h"
63 #include "../libnrtype/font-instance.h"
64 #include "../libnrtype/font-lister.h"
65 #include "../live_effects/effect.h"
66 #include "../live_effects/lpe-angle_bisector.h"
67 #include "../live_effects/lpe-line_segment.h"
68 #include "../lpe-tool-context.h"
69 #include "../mod360.h"
70 #include "../pen-context.h"
71 #include "../preferences.h"
72 #include "../selection-chemistry.h"
73 #include "../selection.h"
74 #include "select-toolbar.h"
75 #include "../shape-editor.h"
76 #include "../shortcuts.h"
77 #include "../sp-clippath.h"
78 #include "../sp-ellipse.h"
79 #include "../sp-flowtext.h"
80 #include "../sp-mask.h"
81 #include "../sp-namedview.h"
82 #include "../sp-rect.h"
83 #include "../sp-spiral.h"
84 #include "../sp-star.h"
85 #include "../sp-text.h"
86 #include "../style.h"
87 #include "../svg/css-ostringstream.h"
88 #include "../text-context.h"
89 #include "../text-editing.h"
90 #include "../tools-switch.h"
91 #include "../tweak-context.h"
92 #include "../spray-context.h"
93 #include "../ui/dialog/calligraphic-profile-rename.h"
94 #include "../ui/icon-names.h"
95 #include "../ui/tool/control-point-selection.h"
96 #include "../ui/tool/node-tool.h"
97 #include "../ui/tool/multi-path-manipulator.h"
98 #include "../ui/widget/style-swatch.h"
99 #include "../verbs.h"
100 #include "../widgets/button.h"
101 #include "../widgets/spinbutton-events.h"
102 #include "../widgets/spw-utilities.h"
103 #include "../widgets/widget-sizes.h"
104 #include "../xml/attribute-record.h"
105 #include "../xml/node-event-vector.h"
106 #include "../xml/repr.h"
107 #include "ui/uxmanager.h"
109 #include "toolbox.h"
111 //#define DEBUG_TEXT
113 using Inkscape::UnitTracker;
114 using Inkscape::UI::UXManager;
116 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
117 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
119 enum BarId {
120     BAR_TOOL = 0,
121     BAR_AUX,
122     BAR_COMMANDS,
123     BAR_SNAP,
124 };
126 #define BAR_ID_KEY "BarIdValue"
127 #define HANDLE_POS_MARK "x-inkscape-pos"
129 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
139 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
142 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
143 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
144 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
145 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
146 static void       sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
147 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
149 using Inkscape::UI::ToolboxFactory;
152 Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
153     static Inkscape::IconSize sizeChoices[] = {
154         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
155         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
156         Inkscape::ICON_SIZE_MENU
157     };
158     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
159     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
160     return sizeChoices[index];
163 static struct {
164     gchar const *type_name;
165     gchar const *data_name;
166     sp_verb_t verb;
167     sp_verb_t doubleclick_verb;
168 } const tools[] = {
169     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
170     { "InkNodeTool",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
171     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
172     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
173     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
174     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
175     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
176     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
177     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
178     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
179     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
180     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
181     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
182     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
183     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
184     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
185     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
186     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
187     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
188     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
189     { NULL, NULL, 0, 0 }
190 };
192 static struct {
193     gchar const *type_name;
194     gchar const *data_name;
195     GtkWidget *(*create_func)(SPDesktop *desktop);
196     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
197     gchar const *ui_name;
198     gint swatch_verb_id;
199     gchar const *swatch_tool;
200     gchar const *swatch_tip;
201 } const aux_toolboxes[] = {
202     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
203       SP_VERB_INVALID, 0, 0},
204     { "InkNodeTool",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
205       SP_VERB_INVALID, 0, 0},
206     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
207       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
208     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
209       SP_VERB_INVALID, 0, 0},
210     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
211       SP_VERB_INVALID, 0, 0},
212     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
213       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
214     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
215       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
216     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
217       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
218     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
219       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
220     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
221       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
222     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
223       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
224     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
225       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
226     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
227       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
228     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
229       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
230     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
231       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
232     { "SPTextContext",   "text_toolbox",   0, sp_text_toolbox_prep, "TextToolbar",
233       SP_VERB_INVALID, 0, 0},
234     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
235       SP_VERB_INVALID, 0, 0},
236     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
237       SP_VERB_INVALID, 0, 0},
238     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
239       SP_VERB_INVALID, 0, 0},
240     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
241       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
242     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
243 };
245 #define TOOLBAR_SLIDER_HINT "full"
247 static gchar const * ui_descr =
248         "<ui>"
249         "  <toolbar name='SelectToolbar'>"
250         "    <toolitem action='EditSelectAll' />"
251         "    <toolitem action='EditSelectAllInAllLayers' />"
252         "    <toolitem action='EditDeselect' />"
253         "    <separator />"
254         "    <toolitem action='ObjectRotate90CCW' />"
255         "    <toolitem action='ObjectRotate90' />"
256         "    <toolitem action='ObjectFlipHorizontally' />"
257         "    <toolitem action='ObjectFlipVertically' />"
258         "    <separator />"
259         "    <toolitem action='SelectionToBack' />"
260         "    <toolitem action='SelectionLower' />"
261         "    <toolitem action='SelectionRaise' />"
262         "    <toolitem action='SelectionToFront' />"
263         "    <separator />"
264         "    <toolitem action='XAction' />"
265         "    <toolitem action='YAction' />"
266         "    <toolitem action='WidthAction' />"
267         "    <toolitem action='LockAction' />"
268         "    <toolitem action='HeightAction' />"
269         "    <toolitem action='UnitsAction' />"
270         "    <separator />"
271         "    <toolitem action='transform_affect_label' />"
272         "    <toolitem action='transform_stroke' />"
273         "    <toolitem action='transform_corners' />"
274         "    <toolitem action='transform_gradient' />"
275         "    <toolitem action='transform_pattern' />"
276         "  </toolbar>"
278         "  <toolbar name='NodeToolbar'>"
279         "    <separator />"
280         "    <toolitem action='NodeInsertAction' />"
281         "    <toolitem action='NodeDeleteAction' />"
282         "    <separator />"
283         "    <toolitem action='NodeJoinAction' />"
284         "    <toolitem action='NodeBreakAction' />"
285         "    <separator />"
286         "    <toolitem action='NodeJoinSegmentAction' />"
287         "    <toolitem action='NodeDeleteSegmentAction' />"
288         "    <separator />"
289         "    <toolitem action='NodeCuspAction' />"
290         "    <toolitem action='NodeSmoothAction' />"
291         "    <toolitem action='NodeSymmetricAction' />"
292         "    <toolitem action='NodeAutoAction' />"
293         "    <separator />"
294         "    <toolitem action='NodeLineAction' />"
295         "    <toolitem action='NodeCurveAction' />"
296         "    <separator />"
297         "    <toolitem action='ObjectToPath' />"
298         "    <toolitem action='StrokeToPath' />"
299         "    <separator />"
300         "    <toolitem action='NodeXAction' />"
301         "    <toolitem action='NodeYAction' />"
302         "    <toolitem action='NodeUnitsAction' />"
303         "    <separator />"
304         "    <toolitem action='ObjectEditClipPathAction' />"
305         "    <toolitem action='ObjectEditMaskPathAction' />"
306         "    <toolitem action='EditNextLPEParameterAction' />"
307         "    <separator />"
308         "    <toolitem action='NodesShowTransformHandlesAction' />"
309         "    <toolitem action='NodesShowHandlesAction' />"
310         "    <toolitem action='NodesShowHelperpath' />"
311         "  </toolbar>"
313         "  <toolbar name='TweakToolbar'>"
314         "    <toolitem action='TweakWidthAction' />"
315         "    <separator />"
316         "    <toolitem action='TweakForceAction' />"
317         "    <toolitem action='TweakPressureAction' />"
318         "    <separator />"
319         "    <toolitem action='TweakModeAction' />"
320         "    <separator />"
321         "    <toolitem action='TweakFidelityAction' />"
322         "    <separator />"
323         "    <toolitem action='TweakChannelsLabel' />"
324         "    <toolitem action='TweakDoH' />"
325         "    <toolitem action='TweakDoS' />"
326         "    <toolitem action='TweakDoL' />"
327         "    <toolitem action='TweakDoO' />"
328         "  </toolbar>"
330         "  <toolbar name='SprayToolbar'>"
331         "    <toolitem action='SprayModeAction' />"
332         "    <separator />"
333         "    <separator />"
334         "    <toolitem action='SprayWidthAction' />"
335         "    <toolitem action='SprayPressureAction' />"
336         "    <toolitem action='SprayPopulationAction' />"
337         "    <separator />"
338         "    <toolitem action='SprayRotationAction' />"
339         "    <toolitem action='SprayScaleAction' />"
340         "    <separator />"
341         "    <toolitem action='SprayStandard_deviationAction' />"
342         "    <toolitem action='SprayMeanAction' />"
343         "  </toolbar>"
345         "  <toolbar name='ZoomToolbar'>"
346         "    <toolitem action='ZoomIn' />"
347         "    <toolitem action='ZoomOut' />"
348         "    <separator />"
349         "    <toolitem action='Zoom1:0' />"
350         "    <toolitem action='Zoom1:2' />"
351         "    <toolitem action='Zoom2:1' />"
352         "    <separator />"
353         "    <toolitem action='ZoomSelection' />"
354         "    <toolitem action='ZoomDrawing' />"
355         "    <toolitem action='ZoomPage' />"
356         "    <toolitem action='ZoomPageWidth' />"
357         "    <separator />"
358         "    <toolitem action='ZoomPrev' />"
359         "    <toolitem action='ZoomNext' />"
360         "  </toolbar>"
362         "  <toolbar name='StarToolbar'>"
363         "    <separator />"
364         "    <toolitem action='StarStateAction' />"
365         "    <separator />"
366         "    <toolitem action='FlatAction' />"
367         "    <separator />"
368         "    <toolitem action='MagnitudeAction' />"
369         "    <toolitem action='SpokeAction' />"
370         "    <toolitem action='RoundednessAction' />"
371         "    <toolitem action='RandomizationAction' />"
372         "    <separator />"
373         "    <toolitem action='StarResetAction' />"
374         "  </toolbar>"
376         "  <toolbar name='RectToolbar'>"
377         "    <toolitem action='RectStateAction' />"
378         "    <toolitem action='RectWidthAction' />"
379         "    <toolitem action='RectHeightAction' />"
380         "    <toolitem action='RadiusXAction' />"
381         "    <toolitem action='RadiusYAction' />"
382         "    <toolitem action='RectUnitsAction' />"
383         "    <separator />"
384         "    <toolitem action='RectResetAction' />"
385         "  </toolbar>"
387         "  <toolbar name='3DBoxToolbar'>"
388         "    <toolitem action='3DBoxAngleXAction' />"
389         "    <toolitem action='3DBoxVPXStateAction' />"
390         "    <separator />"
391         "    <toolitem action='3DBoxAngleYAction' />"
392         "    <toolitem action='3DBoxVPYStateAction' />"
393         "    <separator />"
394         "    <toolitem action='3DBoxAngleZAction' />"
395         "    <toolitem action='3DBoxVPZStateAction' />"
396         "  </toolbar>"
398         "  <toolbar name='SpiralToolbar'>"
399         "    <toolitem action='SpiralStateAction' />"
400         "    <toolitem action='SpiralRevolutionAction' />"
401         "    <toolitem action='SpiralExpansionAction' />"
402         "    <toolitem action='SpiralT0Action' />"
403         "    <separator />"
404         "    <toolitem action='SpiralResetAction' />"
405         "  </toolbar>"
407         "  <toolbar name='PenToolbar'>"
408         "    <toolitem action='FreehandModeActionPen' />"
409         "    <separator />"
410         "    <toolitem action='SetPenShapeAction'/>"
411         "  </toolbar>"
413         "  <toolbar name='PencilToolbar'>"
414         "    <toolitem action='FreehandModeActionPencil' />"
415         "    <separator />"
416         "    <toolitem action='PencilToleranceAction' />"
417         "    <separator />"
418         "    <toolitem action='PencilResetAction' />"
419         "    <separator />"
420         "    <toolitem action='SetPencilShapeAction'/>"
421         "  </toolbar>"
423         "  <toolbar name='CalligraphyToolbar'>"
424         "    <separator />"
425         "    <toolitem action='SetProfileAction'/>"
426         "    <separator />"
427         "    <toolitem action='CalligraphyWidthAction' />"
428         "    <toolitem action='PressureAction' />"
429         "    <toolitem action='TraceAction' />"
430         "    <toolitem action='ThinningAction' />"
431         "    <separator />"
432         "    <toolitem action='AngleAction' />"
433         "    <toolitem action='TiltAction' />"
434         "    <toolitem action='FixationAction' />"
435         "    <separator />"
436         "    <toolitem action='CapRoundingAction' />"
437         "    <separator />"
438         "    <toolitem action='TremorAction' />"
439         "    <toolitem action='WiggleAction' />"
440         "    <toolitem action='MassAction' />"
441         "    <separator />"
442         "  </toolbar>"
444         "  <toolbar name='ArcToolbar'>"
445         "    <toolitem action='ArcStateAction' />"
446         "    <separator />"
447         "    <toolitem action='ArcStartAction' />"
448         "    <toolitem action='ArcEndAction' />"
449         "    <separator />"
450         "    <toolitem action='ArcOpenAction' />"
451         "    <separator />"
452         "    <toolitem action='ArcResetAction' />"
453         "    <separator />"
454         "  </toolbar>"
456         "  <toolbar name='PaintbucketToolbar'>"
457         "    <toolitem action='ChannelsAction' />"
458         "    <separator />"
459         "    <toolitem action='ThresholdAction' />"
460         "    <separator />"
461         "    <toolitem action='OffsetAction' />"
462         "    <toolitem action='PaintbucketUnitsAction' />"
463         "    <separator />"
464         "    <toolitem action='AutoGapAction' />"
465         "    <separator />"
466         "    <toolitem action='PaintbucketResetAction' />"
467         "  </toolbar>"
469         "  <toolbar name='EraserToolbar'>"
470         "    <toolitem action='EraserWidthAction' />"
471         "    <separator />"
472         "    <toolitem action='EraserModeAction' />"
473         "  </toolbar>"
475         "  <toolbar name='TextToolbar'>"
476         "    <toolitem action='TextFontFamilyAction' />"
477         "    <toolitem action='TextFontSizeAction' />"
478         "    <toolitem action='TextBoldAction' />"
479         "    <toolitem action='TextItalicAction' />"
480         "    <separator />"
481         "    <toolitem action='TextAlignAction' />"
482         "    <separator />"
483         "    <toolitem action='TextSuperscriptAction' />"
484         "    <toolitem action='TextSubscriptAction' />"
485         "    <separator />"
486         "    <toolitem action='TextLineHeightAction' />"
487         "    <toolitem action='TextLetterSpacingAction' />"
488         "    <toolitem action='TextWordSpacingAction' />"
489         "    <toolitem action='TextDxAction' />"
490         "    <toolitem action='TextDyAction' />"
491         "    <toolitem action='TextRotationAction' />"
492         "    <separator />"
493         "    <toolitem action='TextOrientationAction' />"
494         "  </toolbar>"
496         "  <toolbar name='LPEToolToolbar'>"
497         "    <toolitem action='LPEToolModeAction' />"
498         "    <separator />"
499         "    <toolitem action='LPEShowBBoxAction' />"
500         "    <toolitem action='LPEBBoxFromSelectionAction' />"
501         "    <separator />"
502         "    <toolitem action='LPELineSegmentAction' />"
503         "    <separator />"
504         "    <toolitem action='LPEMeasuringAction' />"
505         "    <toolitem action='LPEToolUnitsAction' />"
506         "    <separator />"
507         "    <toolitem action='LPEOpenLPEDialogAction' />"
508         "  </toolbar>"
510         "  <toolbar name='DropperToolbar'>"
511         "    <toolitem action='DropperOpacityAction' />"
512         "    <toolitem action='DropperPickAlphaAction' />"
513         "    <toolitem action='DropperSetAlphaAction' />"
514         "  </toolbar>"
516         "  <toolbar name='ConnectorToolbar'>"
517 //        "    <toolitem action='ConnectorEditModeAction' />"
518         "    <toolitem action='ConnectorAvoidAction' />"
519         "    <toolitem action='ConnectorIgnoreAction' />"
520         "    <toolitem action='ConnectorOrthogonalAction' />"
521         "    <toolitem action='ConnectorCurvatureAction' />"
522         "    <toolitem action='ConnectorSpacingAction' />"
523         "    <toolitem action='ConnectorGraphAction' />"
524         "    <toolitem action='ConnectorLengthAction' />"
525         "    <toolitem action='ConnectorDirectedAction' />"
526         "    <toolitem action='ConnectorOverlapAction' />"
527 //        "    <toolitem action='ConnectorNewConnPointAction' />"
528 //        "    <toolitem action='ConnectorRemoveConnPointAction' />"
529         "  </toolbar>"
531         "</ui>"
534 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
536 static void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
538 static void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
539 static void update_tool_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
541 static void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
542 static void update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
544 static void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
545 static void update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
547 static GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
548                                                                      Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
549                                                                      Inkscape::UI::View::View *view, GtkTooltips *tt);
551 class VerbAction : public Gtk::Action {
552 public:
553     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
555     virtual ~VerbAction();
556     virtual void set_active(bool active = true);
558 protected:
559     virtual Gtk::Widget* create_menu_item_vfunc();
560     virtual Gtk::Widget* create_tool_item_vfunc();
562     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
563     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
565     virtual void on_activate();
567 private:
568     Inkscape::Verb* verb;
569     Inkscape::Verb* verb2;
570     Inkscape::UI::View::View *view;
571     GtkTooltips *tooltips;
572     bool active;
574     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
575 };
578 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
580     Glib::RefPtr<VerbAction> result;
581     SPAction *action = verb->get_action(view);
582     if ( action ) {
583         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
584         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
585     }
587     return result;
590 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
591     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
592     verb(verb),
593     verb2(verb2),
594     view(view),
595     tooltips(tooltips),
596     active(false)
600 VerbAction::~VerbAction()
604 Gtk::Widget* VerbAction::create_menu_item_vfunc()
606 // First call in to get the icon rendered if present in SVG
607     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
608     delete widget;
609     widget = 0;
611     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
612 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
613     return widg;
616 Gtk::Widget* VerbAction::create_tool_item_vfunc()
618 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
619     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small");
620     GtkWidget* toolbox = 0;
621     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
622                                                                           SP_BUTTON_TYPE_TOGGLE,
623                                                                           verb,
624                                                                           verb2,
625                                                                           view,
626                                                                           tooltips );
627     if ( active ) {
628         sp_button_toggle_set_down( SP_BUTTON(button), active);
629     }
630     gtk_widget_show_all( button );
631     Gtk::Widget* wrapped = Glib::wrap(button);
632     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
633     holder->add(*wrapped);
635 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
636     return holder;
639 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
641 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
642     Gtk::Action::connect_proxy_vfunc(proxy);
645 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
647 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
648     Gtk::Action::disconnect_proxy_vfunc(proxy);
651 void VerbAction::set_active(bool active)
653     this->active = active;
654     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
655     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
656         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
657         if (ti) {
658             // *should* have one child that is the SPButton
659             Gtk::Widget* child = ti->get_child();
660             if ( child && SP_IS_BUTTON(child->gobj()) ) {
661                 SPButton* button = SP_BUTTON(child->gobj());
662                 sp_button_toggle_set_down( button, active );
663             }
664         }
665     }
668 void VerbAction::on_activate()
670     if ( verb ) {
671         SPAction *action = verb->get_action(view);
672         if ( action ) {
673             sp_action_perform(action, 0);
674         }
675     }
678 /* Global text entry widgets necessary for update */
679 /* GtkWidget *dropper_rgb_entry,
680           *dropper_opacity_entry ; */
681 // should be made a private member once this is converted to class
683 static void delete_connection(GObject * /*obj*/, sigc::connection *connection)
685     connection->disconnect();
686     delete connection;
689 static void purge_repr_listener( GObject* /*obj*/, GObject* tbl )
691     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
692     if (oldrepr) { // remove old listener
693         sp_repr_remove_listener_by_data(oldrepr, tbl);
694         Inkscape::GC::release(oldrepr);
695         oldrepr = 0;
696         g_object_set_data( tbl, "repr", NULL );
697     }
700 // ------------------------------------------------------
702 /**
703  * A simple mediator class that keeps UI controls matched to the preference values they set.
704  */
705 class PrefPusher : public Inkscape::Preferences::Observer
707 public:
708     /**
709      * Constructor for a boolean value that syncs to the supplied path.
710      * Initializes the widget to the current preference stored state and registers callbacks
711      * for widget changes and preference changes.
712      *
713      * @param act the widget to synchronize preference with.
714      * @param path the path to the preference the widget is synchronized with.
715      * @param callback function to invoke when changes are pushed.
716      * @param cbData data to be passed on to the callback function.
717      */
718     PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*) = 0, GObject *cbData = 0 );
720     /**
721      * Destructor that unregisters the preference callback.
722      */
723     virtual ~PrefPusher();
725     /**
726      * Callback method invoked when the preference setting changes.
727      */
728     virtual void notify(Inkscape::Preferences::Entry const &new_val);
730 private:
731     /**
732      * Callback hook invoked when the widget changes.
733      *
734      * @param act the toggle action widget that was changed.
735      * @param self the PrefPusher instance the callback was registered to.
736      */
737     static void toggleCB( GtkToggleAction *act, PrefPusher *self );
739     /**
740      * Method to handle the widget change.
741      */
742     void handleToggled();
744     GtkToggleAction *act;
745     void (*callback)(GObject*);
746     GObject *cbData;
747     bool freeze;
748 };
750 PrefPusher::PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*), GObject *cbData ) :
751     Observer(path),
752     act(act),
753     callback(callback),
754     cbData(cbData),
755     freeze(false)
757     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggleCB), this);
758     freeze = true;
759     gtk_toggle_action_set_active( act, Inkscape::Preferences::get()->getBool(observed_path) );
760     freeze = false;
762     Inkscape::Preferences::get()->addObserver(*this);
765 PrefPusher::~PrefPusher()
767     Inkscape::Preferences::get()->removeObserver(*this);
770 void PrefPusher::toggleCB( GtkToggleAction * /*act*/, PrefPusher *self )
772     if (self) {
773         self->handleToggled();
774     }
777 void PrefPusher::handleToggled()
779     if (!freeze) {
780         freeze = true;
781         Inkscape::Preferences::get()->setBool(observed_path, gtk_toggle_action_get_active( act ));
782         if (callback) {
783             (*callback)(cbData);
784         }
785         freeze = false;
786     }
789 void PrefPusher::notify(Inkscape::Preferences::Entry const &newVal)
791     bool newBool = newVal.getBool();
792     bool oldBool = gtk_toggle_action_get_active(act);
794     if (!freeze && (newBool != oldBool)) {
795         gtk_toggle_action_set_active( act, newBool );
796     }
799 static void delete_prefspusher(GtkObject * /*obj*/, PrefPusher *watcher )
801     delete watcher;
804 // ------------------------------------------------------
807 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
808                                                              Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
809                                                              Inkscape::UI::View::View *view, GtkTooltips *tt)
811     SPAction *action = verb->get_action(view);
812     if (!action) {
813         return NULL;
814     }
816     SPAction *doubleclick_action;
817     if (doubleclick_verb) {
818         doubleclick_action = doubleclick_verb->get_action(view);
819     } else {
820         doubleclick_action = NULL;
821     }
823     /* fixme: Handle sensitive/unsensitive */
824     /* fixme: Implement sp_button_new_from_action */
825     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
826     gtk_widget_show(b);
829     unsigned int shortcut = sp_shortcut_get_primary(verb);
830     if (shortcut) {
831         gchar key[256];
832         sp_ui_shortcut_string(shortcut, key);
833         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
834         if ( t ) {
835             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
836         }
837         g_free(tip);
838     } else {
839         if ( t ) {
840             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
841         }
842     }
844     return b;
848 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
850     SPAction* targetAction = SP_ACTION(user_data);
851     if ( targetAction ) {
852         sp_action_perform( targetAction, NULL );
853     }
856 static void sp_action_action_set_sensitive(SPAction * /*action*/, unsigned int sensitive, void *data)
858     if ( data ) {
859         GtkAction* act = GTK_ACTION(data);
860         gtk_action_set_sensitive( act, sensitive );
861     }
864 static SPActionEventVector action_event_vector = {
865     {NULL},
866     NULL,
867     NULL,
868     sp_action_action_set_sensitive,
869     NULL,
870     NULL
871 };
873 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
875     GtkAction* act = 0;
877     SPAction* targetAction = verb->get_action(view);
878     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
879     act = GTK_ACTION(inky);
880     gtk_action_set_sensitive( act, targetAction->sensitive );
882     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
884     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
885     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
887     return act;
890 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
892     Inkscape::UI::View::View *view = desktop;
893     gint verbsToUse[] = {
894         // disabled until we have icons for them:
895         //find
896         //SP_VERB_EDIT_TILE,
897         //SP_VERB_EDIT_UNTILE,
898         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
899         SP_VERB_DIALOG_DISPLAY,
900         SP_VERB_DIALOG_FILL_STROKE,
901         SP_VERB_DIALOG_NAMEDVIEW,
902         SP_VERB_DIALOG_TEXT,
903         SP_VERB_DIALOG_XML_EDITOR,
904         SP_VERB_DIALOG_LAYERS,
905         SP_VERB_EDIT_CLONE,
906         SP_VERB_EDIT_COPY,
907         SP_VERB_EDIT_CUT,
908         SP_VERB_EDIT_DUPLICATE,
909         SP_VERB_EDIT_PASTE,
910         SP_VERB_EDIT_REDO,
911         SP_VERB_EDIT_UNDO,
912         SP_VERB_EDIT_UNLINK_CLONE,
913         SP_VERB_FILE_EXPORT,
914         SP_VERB_FILE_IMPORT,
915         SP_VERB_FILE_NEW,
916         SP_VERB_FILE_OPEN,
917         SP_VERB_FILE_PRINT,
918         SP_VERB_FILE_SAVE,
919         SP_VERB_OBJECT_TO_CURVE,
920         SP_VERB_SELECTION_GROUP,
921         SP_VERB_SELECTION_OUTLINE,
922         SP_VERB_SELECTION_UNGROUP,
923         SP_VERB_ZOOM_1_1,
924         SP_VERB_ZOOM_1_2,
925         SP_VERB_ZOOM_2_1,
926         SP_VERB_ZOOM_DRAWING,
927         SP_VERB_ZOOM_IN,
928         SP_VERB_ZOOM_NEXT,
929         SP_VERB_ZOOM_OUT,
930         SP_VERB_ZOOM_PAGE,
931         SP_VERB_ZOOM_PAGE_WIDTH,
932         SP_VERB_ZOOM_PREV,
933         SP_VERB_ZOOM_SELECTION,
934     };
936     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
938     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
939     Glib::RefPtr<Gtk::ActionGroup> mainActions;
940     if ( groups.find(desktop) != groups.end() ) {
941         mainActions = groups[desktop];
942     }
944     if ( !mainActions ) {
945         mainActions = Gtk::ActionGroup::create("main");
946         groups[desktop] = mainActions;
947     }
949     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
950         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
951         if ( verb ) {
952             if (!mainActions->get_action(verb->get_id())) {
953                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
954                 mainActions->add(Glib::wrap(act));
955             }
956         }
957     }
959     if ( !mainActions->get_action("ToolZoom") ) {
960         GtkTooltips *tt = gtk_tooltips_new();
961         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
962             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
963             if ( va ) {
964                 mainActions->add(va);
965                 if ( i == 0 ) {
966                     va->set_active(true);
967                 }
968             }
969         }
970     }
972     return mainActions;
976 static void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
978     gtk_widget_set_size_request( widget,
979                                  widget->allocation.width,
980                                  widget->allocation.height );
983 static void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
985     gtk_widget_set_size_request( widget, -1, -1 );
988 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
990     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
992     gtk_widget_set_sensitive(tb, FALSE);
994     GtkWidget *hb = 0;
995     if ( UXManager::getInstance()->isFloatWindowProblem() ) {
996         hb = gtk_event_box_new(); // A simple, neutral container.
997     } else {
998         hb = gtk_handle_box_new();
999         gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
1000         gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
1001         gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
1002     }
1004     gtk_container_add(GTK_CONTAINER(hb), tb);
1005     gtk_widget_show(GTK_WIDGET(tb));
1007     sigc::connection* conn = new sigc::connection;
1008     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
1010     if ( GTK_IS_HANDLE_BOX(hb) ) {
1011         g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
1012         g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
1013     }
1015     gpointer val = GINT_TO_POINTER(id);
1016     g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
1018     return hb;
1021 GtkWidget *ToolboxFactory::createToolToolbox()
1023     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1025     return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
1028 GtkWidget *ToolboxFactory::createAuxToolbox()
1030     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1032     return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
1035 //####################################
1036 //# Commands Bar
1037 //####################################
1039 GtkWidget *ToolboxFactory::createCommandsToolbox()
1041     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1043     return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
1046 GtkWidget *ToolboxFactory::createSnapToolbox()
1048     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1050     return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
1053 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1054                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
1055                                                        Glib::ustring const &path, gdouble def,
1056                                                        GtkWidget *focusTarget,
1057                                                        GtkWidget *us,
1058                                                        GObject *dataKludge,
1059                                                        gboolean altx, gchar const *altx_mark,
1060                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1061                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1062                                                        void (*callback)(GtkAdjustment *, GObject *),
1063                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1065     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1066     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
1067                                                              lower, upper, step, page, 0 ) );
1068     if (us) {
1069         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1070     }
1072     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1074     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1075     if ( shortLabel ) {
1076         g_object_set( act, "short_label", shortLabel, NULL );
1077     }
1079     if ( (descrCount > 0) && descrLabels && descrValues ) {
1080         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1081     }
1083     if ( focusTarget ) {
1084         ege_adjustment_action_set_focuswidget( act, focusTarget );
1085     }
1087     if ( altx && altx_mark ) {
1088         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1089     }
1091     if ( dataKludge ) {
1092         // Rather lame, but it's the only place where we need to get the entry name
1093         // but we don't have an Entry
1094         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
1095     }
1097     // Using a cast just to make sure we pass in the right kind of function pointer
1098     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1100     return act;
1104 //####################################
1105 //# node editing callbacks
1106 //####################################
1108 /** Temporary hack: Returns the node tool in the active desktop.
1109  * Will go away during tool refactoring. */
1110 static InkNodeTool *get_node_tool()
1112     InkNodeTool *tool = 0;
1113     if (SP_ACTIVE_DESKTOP ) {
1114         SPEventContext *ec = SP_ACTIVE_DESKTOP->event_context;
1115         if (INK_IS_NODE_TOOL(ec)) {
1116             tool = static_cast<InkNodeTool*>(ec);
1117         }
1118     }
1119     return tool;
1122 static void sp_node_path_edit_add(void)
1124     InkNodeTool *nt = get_node_tool();
1125     if (nt) {
1126         nt->_multipath->insertNodes();
1127     }
1130 static void sp_node_path_edit_delete(void)
1132     InkNodeTool *nt = get_node_tool();
1133     if (nt) {
1134         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1135         nt->_multipath->deleteNodes(prefs->getBool("/tools/nodes/delete_preserves_shape", true));
1136     }
1139 static void sp_node_path_edit_delete_segment(void)
1141     InkNodeTool *nt = get_node_tool();
1142     if (nt) {
1143         nt->_multipath->deleteSegments();
1144     }
1147 static void sp_node_path_edit_break(void)
1149     InkNodeTool *nt = get_node_tool();
1150     if (nt) {
1151         nt->_multipath->breakNodes();
1152     }
1155 static void sp_node_path_edit_join(void)
1157     InkNodeTool *nt = get_node_tool();
1158     if (nt) {
1159         nt->_multipath->joinNodes();
1160     }
1163 static void sp_node_path_edit_join_segment(void)
1165     InkNodeTool *nt = get_node_tool();
1166     if (nt) {
1167         nt->_multipath->joinSegments();
1168     }
1171 static void sp_node_path_edit_toline(void)
1173     InkNodeTool *nt = get_node_tool();
1174     if (nt) {
1175         nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT);
1176     }
1179 static void sp_node_path_edit_tocurve(void)
1181     InkNodeTool *nt = get_node_tool();
1182     if (nt) {
1183         nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER);
1184     }
1187 static void sp_node_path_edit_cusp(void)
1189     InkNodeTool *nt = get_node_tool();
1190     if (nt) {
1191         nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP);
1192     }
1195 static void sp_node_path_edit_smooth(void)
1197     InkNodeTool *nt = get_node_tool();
1198     if (nt) {
1199         nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH);
1200     }
1203 static void sp_node_path_edit_symmetrical(void)
1205     InkNodeTool *nt = get_node_tool();
1206     if (nt) {
1207         nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC);
1208     }
1211 static void sp_node_path_edit_auto(void)
1213     InkNodeTool *nt = get_node_tool();
1214     if (nt) {
1215         nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO);
1216     }
1219 static void sp_node_path_edit_nextLPEparam(GtkAction * /*act*/, gpointer data) {
1220     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1223 /* is called when the node selection is modified */
1224 static void sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1226     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1227     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1228     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1229     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1231     // quit if run by the attr_changed listener
1232     if (g_object_get_data( tbl, "freeze" )) {
1233         return;
1234     }
1236     // in turn, prevent listener from responding
1237     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1239     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1240     SPUnit const *unit = tracker->getActiveUnit();
1242     InkNodeTool *nt = get_node_tool();
1243     if (!nt || nt->_selected_nodes->empty()) {
1244         // no path selected
1245         gtk_action_set_sensitive(xact, FALSE);
1246         gtk_action_set_sensitive(yact, FALSE);
1247     } else {
1248         gtk_action_set_sensitive(xact, TRUE);
1249         gtk_action_set_sensitive(yact, TRUE);
1250         Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1251         Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1252         Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint();
1254         if (oldx != mid[Geom::X]) {
1255             gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit));
1256         }
1257         if (oldy != mid[Geom::Y]) {
1258             gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit));
1259         }
1260     }
1262     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1265 static void sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d)
1267     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1268     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1270     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1271     SPUnit const *unit = tracker->getActiveUnit();
1273     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
1274         prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"),
1275             sp_units_get_pixels(adj->value, *unit));
1276     }
1278     // quit if run by the attr_changed listener
1279     if (g_object_get_data( tbl, "freeze" )) {
1280         return;
1281     }
1283     // in turn, prevent listener from responding
1284     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1286     InkNodeTool *nt = get_node_tool();
1287     if (nt && !nt->_selected_nodes->empty()) {
1288         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1289         double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d];
1290         Geom::Point delta(0,0);
1291         delta[d] = val - oldval;
1292         nt->_multipath->move(delta);
1293     }
1295     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1298 static void sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1300     sp_node_path_value_changed(adj, tbl, Geom::X);
1303 static void sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1305     sp_node_path_value_changed(adj, tbl, Geom::Y);
1308 static void sp_node_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
1310     {
1311     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1312     SPItem *item = selection->singleItem();
1313     if (item && SP_IS_LPE_ITEM(item)) {
1314        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1315            gtk_action_set_sensitive(w, TRUE);
1316        } else {
1317            gtk_action_set_sensitive(w, FALSE);
1318        }
1319     } else {
1320        gtk_action_set_sensitive(w, FALSE);
1321     }
1322     }
1325 static void sp_node_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1327     sp_node_toolbox_sel_changed (selection, tbl);
1332 //################################
1333 //##    Node Editing Toolbox    ##
1334 //################################
1336 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1338     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1339     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1340     g_object_set_data( holder, "tracker", tracker );
1342     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1344     {
1345         InkAction* inky = ink_action_new( "NodeInsertAction",
1346                                           _("Insert node"),
1347                                           _("Insert new nodes into selected segments"),
1348                                           INKSCAPE_ICON_NODE_ADD,
1349                                           secondarySize );
1350         g_object_set( inky, "short_label", _("Insert"), NULL );
1351         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1352         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1353     }
1355     {
1356         InkAction* inky = ink_action_new( "NodeDeleteAction",
1357                                           _("Delete node"),
1358                                           _("Delete selected nodes"),
1359                                           INKSCAPE_ICON_NODE_DELETE,
1360                                           secondarySize );
1361         g_object_set( inky, "short_label", _("Delete"), NULL );
1362         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1363         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1364     }
1366     {
1367         InkAction* inky = ink_action_new( "NodeJoinAction",
1368                                           _("Join nodes"),
1369                                           _("Join selected nodes"),
1370                                           INKSCAPE_ICON_NODE_JOIN,
1371                                           secondarySize );
1372         g_object_set( inky, "short_label", _("Join"), NULL );
1373         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1374         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1375     }
1377     {
1378         InkAction* inky = ink_action_new( "NodeBreakAction",
1379                                           _("Break nodes"),
1380                                           _("Break path at selected nodes"),
1381                                           INKSCAPE_ICON_NODE_BREAK,
1382                                           secondarySize );
1383         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1384         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1385     }
1388     {
1389         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1390                                           _("Join with segment"),
1391                                           _("Join selected endnodes with a new segment"),
1392                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1393                                           secondarySize );
1394         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1395         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1396     }
1398     {
1399         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1400                                           _("Delete segment"),
1401                                           _("Delete segment between two non-endpoint nodes"),
1402                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1403                                           secondarySize );
1404         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1405         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1406     }
1408     {
1409         InkAction* inky = ink_action_new( "NodeCuspAction",
1410                                           _("Node Cusp"),
1411                                           _("Make selected nodes corner"),
1412                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1413                                           secondarySize );
1414         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1415         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1416     }
1418     {
1419         InkAction* inky = ink_action_new( "NodeSmoothAction",
1420                                           _("Node Smooth"),
1421                                           _("Make selected nodes smooth"),
1422                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1423                                           secondarySize );
1424         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1425         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1426     }
1428     {
1429         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1430                                           _("Node Symmetric"),
1431                                           _("Make selected nodes symmetric"),
1432                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1433                                           secondarySize );
1434         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1435         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1436     }
1438     {
1439         InkAction* inky = ink_action_new( "NodeAutoAction",
1440                                           _("Node Auto"),
1441                                           _("Make selected nodes auto-smooth"),
1442                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1443                                           secondarySize );
1444         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1445         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1446     }
1448     {
1449         InkAction* inky = ink_action_new( "NodeLineAction",
1450                                           _("Node Line"),
1451                                           _("Make selected segments lines"),
1452                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1453                                           secondarySize );
1454         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1455         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1456     }
1458     {
1459         InkAction* inky = ink_action_new( "NodeCurveAction",
1460                                           _("Node Curve"),
1461                                           _("Make selected segments curves"),
1462                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1463                                           secondarySize );
1464         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1465         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1466     }
1468     {
1469         InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction",
1470                                                       _("Show Transform Handles"),
1471                                                       _("Show transformation handles for selected nodes"),
1472                                                       "node-transform",
1473                                                       secondarySize );
1474         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1475         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_transform_handles");
1476         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1477     }
1479     {
1480         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1481                                                       _("Show Handles"),
1482                                                       _("Show Bezier handles of selected nodes"),
1483                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1484                                                       secondarySize );
1485         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1486         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_handles");
1487         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1488     }
1490     {
1491         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1492                                                       _("Show Outline"),
1493                                                       _("Show path outline (without path effects)"),
1494                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1495                                                       secondarySize );
1496         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1497         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_outline");
1498         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1499     }
1501     {
1502         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1503                                           _("Next path effect parameter"),
1504                                           _("Show next editable path effect parameter"),
1505                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1506                                           secondarySize );
1507         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1508         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1509         g_object_set_data( holder, "nodes_lpeedit", inky);
1510     }
1512     {
1513         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction",
1514                                           _("Edit clipping paths"),
1515                                           _("Show clipping path(s) of selected object(s)"),
1516                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1517                                           secondarySize );
1518         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1519         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_clipping_paths");
1520         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1521     }
1523     {
1524         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction",
1525                                           _("Edit masks"),
1526                                           _("Show mask(s) of selected object(s)"),
1527                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1528                                           secondarySize );
1529         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1530         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_masks");
1531         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1532     }
1534     /* X coord of selected node(s) */
1535     {
1536         EgeAdjustmentAction* eact = 0;
1537         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1538         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1539         eact = create_adjustment_action( "NodeXAction",
1540                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1541                                          "/tools/nodes/Xcoord", 0,
1542                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1543                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1544                                          labels, values, G_N_ELEMENTS(labels),
1545                                          sp_node_path_x_value_changed );
1546         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1547         g_object_set_data( holder, "nodes_x_action", eact );
1548         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1549         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1550     }
1552     /* Y coord of selected node(s) */
1553     {
1554         EgeAdjustmentAction* eact = 0;
1555         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1556         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1557         eact = create_adjustment_action( "NodeYAction",
1558                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1559                                          "/tools/nodes/Ycoord", 0,
1560                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1561                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1562                                          labels, values, G_N_ELEMENTS(labels),
1563                                          sp_node_path_y_value_changed );
1564         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1565         g_object_set_data( holder, "nodes_y_action", eact );
1566         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1567         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1568     }
1570     // add the units menu
1571     {
1572         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1573         gtk_action_group_add_action( mainActions, act );
1574     }
1577     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1579     //watch selection
1580     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1582     sigc::connection *c_selection_changed =
1583         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1584                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1585     pool->add_connection ("selection-changed", c_selection_changed);
1587     sigc::connection *c_selection_modified =
1588         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1589                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1590     pool->add_connection ("selection-modified", c_selection_modified);
1592     sigc::connection *c_subselection_changed =
1593         new sigc::connection (desktop->connectToolSubselectionChanged
1594                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1595     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1597     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1599     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1600 } // end of sp_node_toolbox_prep()
1603 //########################
1604 //##    Zoom Toolbox    ##
1605 //########################
1607 static void sp_zoom_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1609     // no custom GtkAction setup needed
1610 } // end of sp_zoom_toolbox_prep()
1612 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1614     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1615                                                                               "event_context_connection"));
1617     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1619     SetupFunction setup_func = 0;
1620     UpdateFunction update_func = 0;
1622     switch (id) {
1623         case BAR_TOOL:
1624             setup_func = setup_tool_toolbox;
1625             update_func = update_tool_toolbox;
1626             break;
1628         case BAR_AUX:
1629             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1630             setup_func = setup_aux_toolbox;
1631             update_func = update_aux_toolbox;
1632             break;
1634         case BAR_COMMANDS:
1635             setup_func = setup_commands_toolbox;
1636             update_func = update_commands_toolbox;
1637             break;
1639         case BAR_SNAP:
1640             setup_func = setup_snap_toolbox;
1641             update_func = updateSnapToolbox;
1642             break;
1643         default:
1644             g_warning("Unexpected toolbox id encountered.");
1645     }
1647     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1648     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1650     if (old_desktop) {
1651         GList *children, *iter;
1653         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1654         for ( iter = children ; iter ; iter = iter->next ) {
1655             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1656         }
1657         g_list_free(children);
1658     }
1660     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1662     if (desktop && setup_func && update_func) {
1663         gtk_widget_set_sensitive(toolbox, TRUE);
1664         setup_func(toolbox, desktop);
1665         update_func(desktop, desktop->event_context, toolbox);
1666         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1667     } else {
1668         gtk_widget_set_sensitive(toolbox, FALSE);
1669     }
1671 } // end of sp_toolbox_set_desktop()
1674 static void setupToolboxCommon( GtkWidget *toolbox,
1675                                 SPDesktop *desktop,
1676                                 gchar const *descr,
1677                                 gchar const* toolbarName,
1678                                 gchar const* sizePref )
1680     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1681     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1683     GtkUIManager* mgr = gtk_ui_manager_new();
1684     GError* errVal = 0;
1686     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1688     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1689     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1691     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1692     if ( prefs->getBool("/toolbox/icononly", true) ) {
1693         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1694     }
1696     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1697     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1699     if (GTK_IS_HANDLE_BOX(toolbox)) {
1700         // g_message("GRABBING ORIENTATION   [%s]", toolbarName);
1701         GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1702         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1703     } else {
1704         GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK )));
1705         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1706     }
1707     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1708     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1710     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1712     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1713     if ( child ) {
1714         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1715     }
1717     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1720 #define noDUMP_DETAILS 1
1722 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1724 #if DUMP_DETAILS
1725     g_message("Set orientation for %p to be %d", toolbox, orientation);
1726     GType type = GTK_WIDGET_TYPE(toolbox);
1727     g_message("        [%s]", g_type_name(type));
1728     g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1729 #endif
1731     GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1732     GtkHandleBox* handleBox = 0;
1734     if (GTK_IS_BIN(toolbox)) {
1735 #if DUMP_DETAILS
1736         g_message("            is a BIN");
1737 #endif // DUMP_DETAILS
1738         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1739         if (child) {
1740 #if DUMP_DETAILS
1741             GType type2 = GTK_WIDGET_TYPE(child);
1742             g_message("            child    [%s]", g_type_name(type2));
1743 #endif // DUMP_DETAILS
1745             if (GTK_IS_BOX(child)) {
1746 #if DUMP_DETAILS
1747                 g_message("                is a BOX");
1748 #endif // DUMP_DETAILS
1750                 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1751                 if (children) {
1752                     for (GList* curr = children; curr; curr = g_list_next(curr)) {
1753                         GtkWidget* child2 = GTK_WIDGET(curr->data);
1754 #if DUMP_DETAILS
1755                         GType type3 = GTK_WIDGET_TYPE(child2);
1756                         g_message("                child2   [%s]", g_type_name(type3));
1757 #endif // DUMP_DETAILS
1759                         if (GTK_IS_CONTAINER(child2)) {
1760                             GList* children2 = gtk_container_get_children(GTK_CONTAINER(child2));
1761                             if (children2) {
1762                                 for (GList* curr2 = children2; curr2; curr2 = g_list_next(curr2)) {
1763                                     GtkWidget* child3 = GTK_WIDGET(curr2->data);
1764 #if DUMP_DETAILS
1765                                     GType type4 = GTK_WIDGET_TYPE(child3);
1766                                     g_message("                    child3   [%s]", g_type_name(type4));
1767 #endif // DUMP_DETAILS
1768                                     if (GTK_IS_TOOLBAR(child3)) {
1769                                         GtkToolbar* childBar = GTK_TOOLBAR(child3);
1770                                         gtk_toolbar_set_orientation(childBar, orientation);
1771                                     }
1772                                 }
1773                                 g_list_free(children2);
1774                             }
1775                         }
1778                         if (GTK_IS_TOOLBAR(child2)) {
1779                             GtkToolbar* childBar = GTK_TOOLBAR(child2);
1780                             gtk_toolbar_set_orientation(childBar, orientation);
1781                             if (GTK_IS_HANDLE_BOX(toolbox)) {
1782                                 handleBox = GTK_HANDLE_BOX(toolbox);
1783                             }
1784                         } else {
1785                             g_message("need to add dynamic switch");
1786                         }
1787                     }
1788                     g_list_free(children);
1789                 } else {
1790                     // The call is being made before the toolbox proper has been setup.
1791                     if (GTK_IS_HANDLE_BOX(toolbox)) {
1792                         handleBox = GTK_HANDLE_BOX(toolbox);
1793                     } else {
1794                         g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos));
1795                     }
1796                 }
1797             } else if (GTK_IS_TOOLBAR(child)) {
1798                 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1799                 gtk_toolbar_set_orientation( toolbar, orientation );
1800                 if (GTK_IS_HANDLE_BOX(toolbox)) {
1801                     handleBox = GTK_HANDLE_BOX(toolbox);
1802                 }
1803             }
1804         }
1805     }
1807     if (handleBox) {
1808         gtk_handle_box_set_handle_position(handleBox, pos);
1809     }
1812 void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1814     gchar const * descr =
1815         "<ui>"
1816         "  <toolbar name='ToolToolbar'>"
1818         "   <!-- Basics -->"
1819         "    <toolitem action='ToolSelector' />"
1820         "    <toolitem action='ToolNode' />"
1821         "    <toolitem action='ToolTweak' />"
1822         "    <toolitem action='ToolZoom' />"
1824         "   <!-- Shapes -->"
1825         "    <toolitem action='ToolRect' />"
1826         "    <toolitem action='Tool3DBox' />"
1827         "    <toolitem action='ToolArc' />"
1828         "    <toolitem action='ToolStar' />"
1829         "    <toolitem action='ToolSpiral' />"
1831         "   <!-- Paths -->"
1832         "    <toolitem action='ToolPencil' />"
1833         "    <toolitem action='ToolPen' />"
1834         "    <toolitem action='ToolCalligraphic' />"
1836         "   <!-- Text -->"
1837         "    <toolitem action='ToolText' />"
1839         "   <!-- Paint large areas -->"
1840         "    <toolitem action='ToolSpray' />"
1841         "    <toolitem action='ToolEraser' />"
1843         "   <!-- Fill -->"
1844         "    <toolitem action='ToolPaintBucket' />"
1845         "    <toolitem action='ToolGradient' />"
1846         "    <toolitem action='ToolDropper' />"
1848         "    <toolitem action='ToolConnector' />"
1850 //        "    <toolitem action='ToolLPETool' />"
1851         "  </toolbar>"
1852         "</ui>";
1854     setupToolboxCommon( toolbox, desktop, descr,
1855                         "/ui/ToolToolbar",
1856                         "/toolbox/tools/small");
1859 void update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget * /*toolbox*/ )
1861     gchar const *const tname = ( eventcontext
1862                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1863                                  : NULL );
1864     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1866     for (int i = 0 ; tools[i].type_name ; i++ ) {
1867         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1868         if ( act ) {
1869             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1870             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1871             if ( verbAct ) {
1872                 verbAct->set_active(setActive);
1873             }
1874         }
1875     }
1878 void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1880     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1881     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1882     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1883     GtkUIManager* mgr = gtk_ui_manager_new();
1884     GError* errVal = 0;
1885     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1886     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1888     std::map<std::string, GtkWidget*> dataHolders;
1890     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1891         if ( aux_toolboxes[i].prep_func ) {
1892             // converted to GtkActions and UIManager
1894             GtkWidget* kludge = gtk_toolbar_new();
1895             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1896             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1897             dataHolders[aux_toolboxes[i].type_name] = kludge;
1898             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1899         } else {
1901             GtkWidget *sub_toolbox = 0;
1902             if (aux_toolboxes[i].create_func == NULL) {
1903                 sub_toolbox = sp_empty_toolbox_new(desktop);
1904             } else {
1905                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1906             }
1908             gtk_size_group_add_widget( grouper, sub_toolbox );
1910             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1911             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1913         }
1914     }
1916     // Second pass to create toolbars *after* all GtkActions are created
1917     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1918         if ( aux_toolboxes[i].prep_func ) {
1919             // converted to GtkActions and UIManager
1921             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1923             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1924             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1926             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1927             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1928             g_free( tmp );
1929             tmp = 0;
1931             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1932                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1933             }
1935             Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1936             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1938             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1940             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1941                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1942                 swatch->setDesktop( desktop );
1943                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1944                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1945                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1946                 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 );
1947             }
1949             gtk_widget_show_all( holder );
1950             sp_set_font_size_smaller( holder );
1952             gtk_size_group_add_widget( grouper, holder );
1954             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1955             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1956         }
1957     }
1959     g_object_unref( G_OBJECT(grouper) );
1962 void update_aux_toolbox(SPDesktop * /*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1964     gchar const *tname = ( eventcontext
1965                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1966                            : NULL );
1967     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1968         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1969         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1970             gtk_widget_show_all(sub_toolbox);
1971             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1972         } else {
1973             gtk_widget_hide(sub_toolbox);
1974         }
1975     }
1978 void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1980     gchar const * descr =
1981         "<ui>"
1982         "  <toolbar name='CommandsToolbar'>"
1983         "    <toolitem action='FileNew' />"
1984         "    <toolitem action='FileOpen' />"
1985         "    <toolitem action='FileSave' />"
1986         "    <toolitem action='FilePrint' />"
1987         "    <separator />"
1988         "    <toolitem action='FileImport' />"
1989         "    <toolitem action='FileExport' />"
1990         "    <separator />"
1991         "    <toolitem action='EditUndo' />"
1992         "    <toolitem action='EditRedo' />"
1993         "    <separator />"
1994         "    <toolitem action='EditCopy' />"
1995         "    <toolitem action='EditCut' />"
1996         "    <toolitem action='EditPaste' />"
1997         "    <separator />"
1998         "    <toolitem action='ZoomSelection' />"
1999         "    <toolitem action='ZoomDrawing' />"
2000         "    <toolitem action='ZoomPage' />"
2001         "    <separator />"
2002         "    <toolitem action='EditDuplicate' />"
2003         "    <toolitem action='EditClone' />"
2004         "    <toolitem action='EditUnlinkClone' />"
2005         "    <separator />"
2006         "    <toolitem action='SelectionGroup' />"
2007         "    <toolitem action='SelectionUnGroup' />"
2008         "    <separator />"
2009         "    <toolitem action='DialogFillStroke' />"
2010         "    <toolitem action='DialogText' />"
2011         "    <toolitem action='DialogLayers' />"
2012         "    <toolitem action='DialogXMLEditor' />"
2013         "    <toolitem action='DialogAlignDistribute' />"
2014         "    <separator />"
2015         "    <toolitem action='DialogPreferences' />"
2016         "    <toolitem action='DialogDocumentProperties' />"
2017         "  </toolbar>"
2018         "</ui>";
2020     setupToolboxCommon( toolbox, desktop, descr,
2021                         "/ui/CommandsToolbar",
2022                         "/toolbox/small" );
2025 void update_commands_toolbox(SPDesktop * /*desktop*/, SPEventContext * /*eventcontext*/, GtkWidget * /*toolbox*/)
2029 static void toggle_snap_callback(GtkToggleAction *act, gpointer data) //data points to the toolbox
2031     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
2032         return;
2033     }
2035     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
2036     g_assert(ptr != NULL);
2038     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
2039     SPNamedView *nv = sp_desktop_namedview(dt);
2040     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
2042     if (dt == NULL || nv == NULL) {
2043         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
2044         return;
2045     }
2047     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
2049     if (repr == NULL) {
2050         g_warning("This namedview doesn't have a xml representation attached!");
2051         return;
2052     }
2054     bool saved = SPDocumentUndo::get_undo_sensitive(doc);
2055         SPDocumentUndo::set_undo_sensitive(doc, false);
2057     bool v = false;
2058     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
2060     switch (attr) {
2061         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
2062             dt->toggleSnapGlobal();
2063             break;
2064         case SP_ATTR_INKSCAPE_SNAP_BBOX:
2065             v = nv->snap_manager.snapprefs.getSnapModeBBox();
2066             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
2067             break;
2068         case SP_ATTR_INKSCAPE_BBOX_PATHS:
2069             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
2070             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
2071             break;
2072         case SP_ATTR_INKSCAPE_BBOX_NODES:
2073             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
2074             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
2075             break;
2076         case SP_ATTR_INKSCAPE_SNAP_NODES:
2077             v = nv->snap_manager.snapprefs.getSnapModeNode();
2078             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
2079             break;
2080         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
2081             v = nv->snap_manager.snapprefs.getSnapToItemPath();
2082             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
2083             break;
2084         case SP_ATTR_INKSCAPE_OBJECT_NODES:
2085             v = nv->snap_manager.snapprefs.getSnapToItemNode();
2086             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
2087             break;
2088         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
2089             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
2090             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2091             break;
2092         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2093             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2094             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2095             break;
2096         case SP_ATTR_INKSCAPE_SNAP_CENTER:
2097             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2098             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2099             break;
2100         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2101             v = nv->snap_manager.snapprefs.getSnapToGrids();
2102             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2103             break;
2104         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2105             v = nv->snap_manager.snapprefs.getSnapToGuides();
2106             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2107             break;
2108         case SP_ATTR_INKSCAPE_SNAP_PAGE:
2109             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2110             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2111             break;
2112             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2113               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2114               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2115               break;*/
2116         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2117             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2118             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2119             break;
2120         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2121             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2122             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2123             break;
2124         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2125             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2126             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2127             break;
2128         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2129             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2130             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2131             break;
2132         default:
2133             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2134             break;
2135     }
2137     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2138     doc->setModifiedSinceSave();
2140         SPDocumentUndo::set_undo_sensitive(doc, saved);
2143 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2145     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2147     gchar const * descr =
2148         "<ui>"
2149         "  <toolbar name='SnapToolbar'>"
2150         "    <toolitem action='ToggleSnapGlobal' />"
2151         "    <separator />"
2152         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2153         "    <toolitem action='ToggleSnapToBBoxPath' />"
2154         "    <toolitem action='ToggleSnapToBBoxNode' />"
2155         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2156         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2157         "    <separator />"
2158         "    <toolitem action='ToggleSnapFromNode' />"
2159         "    <toolitem action='ToggleSnapToItemPath' />"
2160         "    <toolitem action='ToggleSnapToPathIntersections' />"
2161         "    <toolitem action='ToggleSnapToItemNode' />"
2162         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2163         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2164         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2165         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2166         "    <separator />"
2167         "    <toolitem action='ToggleSnapToPageBorder' />"
2168         "    <toolitem action='ToggleSnapToGrids' />"
2169         "    <toolitem action='ToggleSnapToGuides' />"
2170         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2171         "  </toolbar>"
2172         "</ui>";
2174     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2176     {
2177         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2178                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2179                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2181         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2182         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2183     }
2185     {
2186         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2187                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2188                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2190         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2191         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2192     }
2194     {
2195         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2196                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2197                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2199         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2200         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2201     }
2203     {
2204         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2205                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2206                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2208         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2209         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2210     }
2212     {
2213         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2214                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2215                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2216                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2218         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2219         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2220     }
2222     {
2223         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2224                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2225                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2227         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2228         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2229     }
2231     {
2232         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2233                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2235         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2236         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2237     }
2239     {
2240         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2241                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2242                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2244         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2245         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2246     }
2248     {
2249         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2250                                                      _("Path intersections"), _("Snap to path intersections"),
2251                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2253         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2254         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2255     }
2257     {
2258         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2259                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2260                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2262         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2263         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2264     }
2266     {
2267         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2268                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2269                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2271         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2272         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2273     }
2275     {
2276         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2277                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2278                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2280         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2281         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2282     }
2284     {
2285         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2286                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2287                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2289         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2290         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2291     }
2293     {
2294         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2295                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2296                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2298         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2299         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2300     }
2302     {
2303         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2304                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2305                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2307         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2308         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2309     }
2311     {
2312         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2313                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2314                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2316         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2317         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2318     }
2320     {
2321         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2322                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2323                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2325         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2326         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2327     }
2329     /*{
2330       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2331       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2332       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2333       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2335       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2336       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2337       }*/
2339     setupToolboxCommon( toolbox, desktop, descr,
2340                         "/ui/SnapToolbar",
2341                         "/toolbox/secondary" );
2344 Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox)
2346     Glib::ustring name;
2347     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
2348     switch(id) {
2349         case BAR_TOOL:
2350             name = "ToolToolbar";
2351             break;
2352         case BAR_AUX:
2353             name = "AuxToolbar";
2354             break;
2355         case BAR_COMMANDS:
2356             name = "CommandsToolbar";
2357             break;
2358         case BAR_SNAP:
2359             name = "SnapToolbar";
2360             break;
2361     }
2363     return name;
2366 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext * /*eventcontext*/, GtkWidget *toolbox)
2368     g_assert(desktop != NULL);
2369     g_assert(toolbox != NULL);
2371     SPNamedView *nv = sp_desktop_namedview(desktop);
2372     if (nv == NULL) {
2373         g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2374         return;
2375     }
2377     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2379     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2380     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2381     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2382     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2383     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2384     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2385     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2386     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2387     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2388     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2389     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2390     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2391     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2392     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2393     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2394     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2395     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2396     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2399     if (!act1) {
2400         return; // The snap actions haven't been defined yet (might be the case during startup)
2401     }
2403     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2404     // changes in our document because we're only updating the UI;
2405     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2406     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2408     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2409     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2411     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2412     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2413     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2415     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2416     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2417     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2418     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2419     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2420     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2421     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2422     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2424     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2425     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2426     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2428     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2429     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2430     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2431     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2432     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2433     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2434     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2435     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2436     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2437     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2438     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2439     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2440     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2441     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2442     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2444     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2445     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2446     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2447     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2449     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2450     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2451     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2452     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2455     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2458 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2460     gtk_widget_show(toolbox_toplevel);
2461     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2463     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2464     if (!shown_toolbox) {
2465         return;
2466     }
2467     gtk_widget_show(toolbox);
2469     gtk_widget_show_all(shown_toolbox);
2472 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2474     GtkWidget *tbl = gtk_toolbar_new();
2475     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2476     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2478     gtk_widget_show_all(tbl);
2479     sp_set_font_size_smaller (tbl);
2481     return tbl;
2484 #define MODE_LABEL_WIDTH 70
2486 //########################
2487 //##       Star         ##
2488 //########################
2490 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2492     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2494     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
2495         // do not remember prefs if this call is initiated by an undo change, because undoing object
2496         // creation sets bogus values to its attributes before it is deleted
2497         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2498         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2499     }
2501     // quit if run by the attr_changed listener
2502     if (g_object_get_data( dataKludge, "freeze" )) {
2503         return;
2504     }
2506     // in turn, prevent listener from responding
2507     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2509     bool modmade = false;
2511     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2512     GSList const *items = selection->itemList();
2513     for (; items != NULL; items = items->next) {
2514         if (SP_IS_STAR((SPItem *) items->data)) {
2515             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2516             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2517             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2518                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2519                                     + M_PI / (gint)adj->value));
2520             SP_OBJECT((SPItem *) items->data)->updateRepr();
2521             modmade = true;
2522         }
2523     }
2524     if (modmade) {
2525         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2526                          _("Star: Change number of corners"));
2527     }
2529     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2532 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2534     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2536     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
2537         if (!IS_NAN(adj->value)) {
2538             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2539             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2540         }
2541     }
2543     // quit if run by the attr_changed listener
2544     if (g_object_get_data( dataKludge, "freeze" )) {
2545         return;
2546     }
2548     // in turn, prevent listener from responding
2549     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2551     bool modmade = false;
2552     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2553     GSList const *items = selection->itemList();
2554     for (; items != NULL; items = items->next) {
2555         if (SP_IS_STAR((SPItem *) items->data)) {
2556             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2558             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2559             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2560             if (r2 < r1) {
2561                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2562             } else {
2563                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2564             }
2566             SP_OBJECT((SPItem *) items->data)->updateRepr();
2567             modmade = true;
2568         }
2569     }
2571     if (modmade) {
2572         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2573                          _("Star: Change spoke ratio"));
2574     }
2576     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2579 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2581     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2582     bool flat = ege_select_one_action_get_active( act ) == 0;
2584     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
2585         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2586         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2587     }
2589     // quit if run by the attr_changed listener
2590     if (g_object_get_data( dataKludge, "freeze" )) {
2591         return;
2592     }
2594     // in turn, prevent listener from responding
2595     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2597     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2598     GSList const *items = selection->itemList();
2599     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2600     bool modmade = false;
2602     if ( prop_action ) {
2603         gtk_action_set_sensitive( prop_action, !flat );
2604     }
2606     for (; items != NULL; items = items->next) {
2607         if (SP_IS_STAR((SPItem *) items->data)) {
2608             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2609             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2610             SP_OBJECT((SPItem *) items->data)->updateRepr();
2611             modmade = true;
2612         }
2613     }
2615     if (modmade) {
2616         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2617                          flat ? _("Make polygon") : _("Make star"));
2618     }
2620     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2623 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2625     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2627     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
2628         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2629         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2630     }
2632     // quit if run by the attr_changed listener
2633     if (g_object_get_data( dataKludge, "freeze" )) {
2634         return;
2635     }
2637     // in turn, prevent listener from responding
2638     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2640     bool modmade = false;
2642     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2643     GSList const *items = selection->itemList();
2644     for (; items != NULL; items = items->next) {
2645         if (SP_IS_STAR((SPItem *) items->data)) {
2646             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2647             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2648             SP_OBJECT(items->data)->updateRepr();
2649             modmade = true;
2650         }
2651     }
2652     if (modmade) {
2653         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2654                          _("Star: Change rounding"));
2655     }
2657     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2660 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2662     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2664     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
2665         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2666         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2667     }
2669     // quit if run by the attr_changed listener
2670     if (g_object_get_data( dataKludge, "freeze" )) {
2671         return;
2672     }
2674     // in turn, prevent listener from responding
2675     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2677     bool modmade = false;
2679     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2680     GSList const *items = selection->itemList();
2681     for (; items != NULL; items = items->next) {
2682         if (SP_IS_STAR((SPItem *) items->data)) {
2683             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2684             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2685             SP_OBJECT(items->data)->updateRepr();
2686             modmade = true;
2687         }
2688     }
2689     if (modmade) {
2690         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2691                          _("Star: Change randomization"));
2692     }
2694     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2698 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2699                                        gchar const * /*old_value*/, gchar const * /*new_value*/,
2700                                        bool /*is_interactive*/, gpointer data)
2702     GtkWidget *tbl = GTK_WIDGET(data);
2704     // quit if run by the _changed callbacks
2705     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2706         return;
2707     }
2709     // in turn, prevent callbacks from responding
2710     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2712     GtkAdjustment *adj = 0;
2714     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2715     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2717     if (!strcmp(name, "inkscape:randomized")) {
2718         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2719         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2720     } else if (!strcmp(name, "inkscape:rounded")) {
2721         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2722         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2723     } else if (!strcmp(name, "inkscape:flatsided")) {
2724         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2725         char const *flatsides = repr->attribute("inkscape:flatsided");
2726         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2727         if ( flatsides && !strcmp(flatsides,"false") ) {
2728             ege_select_one_action_set_active( flat_action, 1 );
2729             gtk_action_set_sensitive( prop_action, TRUE );
2730         } else {
2731             ege_select_one_action_set_active( flat_action, 0 );
2732             gtk_action_set_sensitive( prop_action, FALSE );
2733         }
2734     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2735         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2736         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2737         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2738         if (r2 < r1) {
2739             gtk_adjustment_set_value(adj, r2/r1);
2740         } else {
2741             gtk_adjustment_set_value(adj, r1/r2);
2742         }
2743     } else if (!strcmp(name, "sodipodi:sides")) {
2744         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2745         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2746     }
2748     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2752 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2754     NULL, /* child_added */
2755     NULL, /* child_removed */
2756     star_tb_event_attr_changed,
2757     NULL, /* content_changed */
2758     NULL  /* order_changed */
2759 };
2762 /**
2763  *  \param selection Should not be NULL.
2764  */
2765 static void
2766 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2768     int n_selected = 0;
2769     Inkscape::XML::Node *repr = NULL;
2771     purge_repr_listener( tbl, tbl );
2773     for (GSList const *items = selection->itemList();
2774          items != NULL;
2775          items = items->next)
2776     {
2777         if (SP_IS_STAR((SPItem *) items->data)) {
2778             n_selected++;
2779             repr = SP_OBJECT_REPR((SPItem *) items->data);
2780         }
2781     }
2783     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2785     if (n_selected == 0) {
2786         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2787     } else if (n_selected == 1) {
2788         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2790         if (repr) {
2791             g_object_set_data( tbl, "repr", repr );
2792             Inkscape::GC::anchor(repr);
2793             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2794             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2795         }
2796     } else {
2797         // FIXME: implement averaging of all parameters for multiple selected stars
2798         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2799         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2800     }
2804 static void sp_stb_defaults( GtkWidget * /*widget*/, GObject *dataKludge )
2806     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2807     // callbacks to lump all the changes for all selected objects in one undo step
2809     GtkAdjustment *adj = 0;
2811     // fixme: make settable in prefs!
2812     gint mag = 5;
2813     gdouble prop = 0.5;
2814     gboolean flat = FALSE;
2815     gdouble randomized = 0;
2816     gdouble rounded = 0;
2818     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2819     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2821     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2822     gtk_action_set_sensitive( sb2, !flat );
2824     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2825     gtk_adjustment_set_value(adj, mag);
2826     gtk_adjustment_value_changed(adj);
2828     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2829     gtk_adjustment_set_value(adj, prop);
2830     gtk_adjustment_value_changed(adj);
2832     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2833     gtk_adjustment_set_value(adj, rounded);
2834     gtk_adjustment_value_changed(adj);
2836     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2837     gtk_adjustment_set_value(adj, randomized);
2838     gtk_adjustment_value_changed(adj);
2842 // public:
2843 void sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2845     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2846     if (wide) {
2847         gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2848     }
2849     GtkWidget *l = gtk_label_new(NULL);
2850     gtk_label_set_markup(GTK_LABEL(l), title);
2851     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2852     if ( GTK_IS_TOOLBAR(tbl) ) {
2853         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2854     } else {
2855         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2856     }
2857     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2861 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2863     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2865     {
2866         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2867         ege_output_action_set_use_markup( act, TRUE );
2868         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2869         g_object_set_data( holder, "mode_action", act );
2870     }
2872     {
2873         EgeAdjustmentAction* eact = 0;
2874         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2875         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2877         /* Flatsided checkbox */
2878         {
2879             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2881             GtkTreeIter iter;
2882             gtk_list_store_append( model, &iter );
2883             gtk_list_store_set( model, &iter,
2884                                 0, _("Polygon"),
2885                                 1, _("Regular polygon (with one handle) instead of a star"),
2886                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2887                                 -1 );
2889             gtk_list_store_append( model, &iter );
2890             gtk_list_store_set( model, &iter,
2891                                 0, _("Star"),
2892                                 1, _("Star instead of a regular polygon (with one handle)"),
2893                                 2, INKSCAPE_ICON_DRAW_STAR,
2894                                 -1 );
2896             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2897             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2898             g_object_set_data( holder, "flat_action", act );
2900             ege_select_one_action_set_appearance( act, "full" );
2901             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2902             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2903             ege_select_one_action_set_icon_column( act, 2 );
2904             ege_select_one_action_set_icon_size( act, secondarySize );
2905             ege_select_one_action_set_tooltip_column( act, 1  );
2907             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2908             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2909         }
2911         /* Magnitude */
2912         {
2913         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2914         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2915         eact = create_adjustment_action( "MagnitudeAction",
2916                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2917                                          "/tools/shapes/star/magnitude", 3,
2918                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2919                                          3, 1024, 1, 5,
2920                                          labels, values, G_N_ELEMENTS(labels),
2921                                          sp_stb_magnitude_value_changed,
2922                                          1.0, 0 );
2923         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2924         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2925         }
2927         /* Spoke ratio */
2928         {
2929         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2930         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2931         eact = create_adjustment_action( "SpokeAction",
2932                                          _("Spoke ratio"), _("Spoke ratio:"),
2933                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2934                                          // Base radius is the same for the closest handle.
2935                                          _("Base radius to tip radius ratio"),
2936                                          "/tools/shapes/star/proportion", 0.5,
2937                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2938                                          0.01, 1.0, 0.01, 0.1,
2939                                          labels, values, G_N_ELEMENTS(labels),
2940                                          sp_stb_proportion_value_changed );
2941         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2942         g_object_set_data( holder, "prop_action", eact );
2943         }
2945         if ( !isFlatSided ) {
2946             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2947         } else {
2948             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2949         }
2951         /* Roundedness */
2952         {
2953         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2954         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2955         eact = create_adjustment_action( "RoundednessAction",
2956                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2957                                          "/tools/shapes/star/rounded", 0.0,
2958                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2959                                          -10.0, 10.0, 0.01, 0.1,
2960                                          labels, values, G_N_ELEMENTS(labels),
2961                                          sp_stb_rounded_value_changed );
2962         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2963         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2964         }
2966         /* Randomization */
2967         {
2968         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2969         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2970         eact = create_adjustment_action( "RandomizationAction",
2971                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2972                                          "/tools/shapes/star/randomized", 0.0,
2973                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2974                                          -10.0, 10.0, 0.001, 0.01,
2975                                          labels, values, G_N_ELEMENTS(labels),
2976                                          sp_stb_randomized_value_changed, 0.1, 3 );
2977         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2978         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2979         }
2980     }
2982     {
2983         /* Reset */
2984         {
2985             GtkAction* act = gtk_action_new( "StarResetAction",
2986                                              _("Defaults"),
2987                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2988                                              GTK_STOCK_CLEAR );
2989             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2990             gtk_action_group_add_action( mainActions, act );
2991             gtk_action_set_sensitive( act, TRUE );
2992         }
2993     }
2995     sigc::connection *connection = new sigc::connection(
2996         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2997         );
2998     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2999     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3003 //########################
3004 //##       Rect         ##
3005 //########################
3007 static void sp_rtb_sensitivize( GObject *tbl )
3009     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
3010     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
3011     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
3013     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
3014         gtk_action_set_sensitive( not_rounded, FALSE );
3015     } else {
3016         gtk_action_set_sensitive( not_rounded, TRUE );
3017     }
3021 static void sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
3022                                  void (*setter)(SPRect *, gdouble))
3024     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3026     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
3027     SPUnit const *unit = tracker->getActiveUnit();
3029     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
3030         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3031         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
3032     }
3034     // quit if run by the attr_changed listener
3035     if (g_object_get_data( tbl, "freeze" )) {
3036         return;
3037     }
3039     // in turn, prevent listener from responding
3040     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
3042     bool modmade = false;
3043     Inkscape::Selection *selection = sp_desktop_selection(desktop);
3044     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
3045         if (SP_IS_RECT(items->data)) {
3046             if (adj->value != 0) {
3047                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
3048             } else {
3049                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
3050             }
3051             modmade = true;
3052         }
3053     }
3055     sp_rtb_sensitivize( tbl );
3057     if (modmade) {
3058         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
3059                                    _("Change rectangle"));
3060     }
3062     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3065 static void sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
3067     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
3070 static void sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
3072     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
3075 static void sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
3077     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
3080 static void sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
3082     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
3087 static void sp_rtb_defaults( GtkWidget * /*widget*/, GObject *obj)
3089     GtkAdjustment *adj = 0;
3091     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
3092     gtk_adjustment_set_value(adj, 0.0);
3093     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
3094     gtk_adjustment_value_changed(adj);
3096     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
3097     gtk_adjustment_set_value(adj, 0.0);
3098     gtk_adjustment_value_changed(adj);
3100     sp_rtb_sensitivize( obj );
3103 static void rect_tb_event_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*name*/,
3104                                        gchar const * /*old_value*/, gchar const * /*new_value*/,
3105                                        bool /*is_interactive*/, gpointer data)
3107     GObject *tbl = G_OBJECT(data);
3109     // quit if run by the _changed callbacks
3110     if (g_object_get_data( tbl, "freeze" )) {
3111         return;
3112     }
3114     // in turn, prevent callbacks from responding
3115     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3117     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3118     SPUnit const *unit = tracker->getActiveUnit();
3120     gpointer item = g_object_get_data( tbl, "item" );
3121     if (item && SP_IS_RECT(item)) {
3122         {
3123             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3124             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3125             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3126         }
3128         {
3129             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3130             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3131             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3132         }
3134         {
3135             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3136             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3137             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3138         }
3140         {
3141             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3142             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3143             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3144         }
3145     }
3147     sp_rtb_sensitivize( tbl );
3149     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3153 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3154     NULL, /* child_added */
3155     NULL, /* child_removed */
3156     rect_tb_event_attr_changed,
3157     NULL, /* content_changed */
3158     NULL  /* order_changed */
3159 };
3161 /**
3162  *  \param selection should not be NULL.
3163  */
3164 static void sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3166     int n_selected = 0;
3167     Inkscape::XML::Node *repr = NULL;
3168     SPItem *item = NULL;
3170     if ( g_object_get_data( tbl, "repr" ) ) {
3171         g_object_set_data( tbl, "item", NULL );
3172     }
3173     purge_repr_listener( tbl, tbl );
3175     for (GSList const *items = selection->itemList();
3176          items != NULL;
3177          items = items->next) {
3178         if (SP_IS_RECT((SPItem *) items->data)) {
3179             n_selected++;
3180             item = (SPItem *) items->data;
3181             repr = SP_OBJECT_REPR(item);
3182         }
3183     }
3185     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3187     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3189     if (n_selected == 0) {
3190         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3192         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3193         gtk_action_set_sensitive(w, FALSE);
3194         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3195         gtk_action_set_sensitive(h, FALSE);
3197     } else if (n_selected == 1) {
3198         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3199         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3201         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3202         gtk_action_set_sensitive(w, TRUE);
3203         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3204         gtk_action_set_sensitive(h, TRUE);
3206         if (repr) {
3207             g_object_set_data( tbl, "repr", repr );
3208             g_object_set_data( tbl, "item", item );
3209             Inkscape::GC::anchor(repr);
3210             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3211             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3212         }
3213     } else {
3214         // FIXME: implement averaging of all parameters for multiple selected
3215         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3216         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3217         sp_rtb_sensitivize( tbl );
3218     }
3222 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3224     EgeAdjustmentAction* eact = 0;
3225     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3227     {
3228         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3229         ege_output_action_set_use_markup( act, TRUE );
3230         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3231         g_object_set_data( holder, "mode_action", act );
3232     }
3234     // rx/ry units menu: create
3235     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3236     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3237     // fixme: add % meaning per cent of the width/height
3238     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3239     g_object_set_data( holder, "tracker", tracker );
3241     /* W */
3242     {
3243         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3244         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3245         eact = create_adjustment_action( "RectWidthAction",
3246                                          _("Width"), _("W:"), _("Width of rectangle"),
3247                                          "/tools/shapes/rect/width", 0,
3248                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3249                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3250                                          labels, values, G_N_ELEMENTS(labels),
3251                                          sp_rtb_width_value_changed );
3252         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3253         g_object_set_data( holder, "width_action", eact );
3254         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3255         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3256     }
3258     /* H */
3259     {
3260         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3261         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3262         eact = create_adjustment_action( "RectHeightAction",
3263                                          _("Height"), _("H:"), _("Height of rectangle"),
3264                                          "/tools/shapes/rect/height", 0,
3265                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3266                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3267                                          labels, values, G_N_ELEMENTS(labels),
3268                                          sp_rtb_height_value_changed );
3269         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3270         g_object_set_data( holder, "height_action", eact );
3271         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3272         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3273     }
3275     /* rx */
3276     {
3277         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3278         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3279         eact = create_adjustment_action( "RadiusXAction",
3280                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3281                                          "/tools/shapes/rect/rx", 0,
3282                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3283                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3284                                          labels, values, G_N_ELEMENTS(labels),
3285                                          sp_rtb_rx_value_changed);
3286         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3287         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3288     }
3290     /* ry */
3291     {
3292         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3293         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3294         eact = create_adjustment_action( "RadiusYAction",
3295                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3296                                          "/tools/shapes/rect/ry", 0,
3297                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3298                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3299                                          labels, values, G_N_ELEMENTS(labels),
3300                                          sp_rtb_ry_value_changed);
3301         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3302         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3303     }
3305     // add the units menu
3306     {
3307         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3308         gtk_action_group_add_action( mainActions, act );
3309     }
3311     /* Reset */
3312     {
3313         InkAction* inky = ink_action_new( "RectResetAction",
3314                                           _("Not rounded"),
3315                                           _("Make corners sharp"),
3316                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3317                                           secondarySize );
3318         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3319         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3320         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3321         g_object_set_data( holder, "not_rounded", inky );
3322     }
3324     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3325     sp_rtb_sensitivize( holder );
3327     sigc::connection *connection = new sigc::connection(
3328         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3329         );
3330     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3331     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3334 //########################
3335 //##       3D Box       ##
3336 //########################
3338 // normalize angle so that it lies in the interval [0,360]
3339 static double box3d_normalize_angle (double a) {
3340     double angle = a + ((int) (a/360.0))*360;
3341     if (angle < 0) {
3342         angle += 360.0;
3343     }
3344     return angle;
3347 static void box3d_set_button_and_adjustment(Persp3D *persp,
3348                                             Proj::Axis axis,
3349                                             GtkAdjustment *adj,
3350                                             GtkAction *act,
3351                                             GtkToggleAction *tact)
3353     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3354     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3355     //       are reset).
3356     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3358     if (is_infinite) {
3359         gtk_toggle_action_set_active(tact, TRUE);
3360         gtk_action_set_sensitive(act, TRUE);
3362         double angle = persp3d_get_infinite_angle(persp, axis);
3363         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3364             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3365         }
3366     } else {
3367         gtk_toggle_action_set_active(tact, FALSE);
3368         gtk_action_set_sensitive(act, FALSE);
3369     }
3372 static void box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data)
3374     if (!persp_repr) {
3375         g_print ("No perspective given to box3d_resync_toolbar().\n");
3376         return;
3377     }
3379     GtkWidget *tbl = GTK_WIDGET(data);
3380     GtkAdjustment *adj = 0;
3381     GtkAction *act = 0;
3382     GtkToggleAction *tact = 0;
3383     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3384     if (!persp) {
3385         // Hmm, is it an error if this happens?
3386         return;
3387     }
3388     {
3389         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3390         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3391         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3393         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3394     }
3395     {
3396         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3397         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3398         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3400         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3401     }
3402     {
3403         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3404         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3405         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3407         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3408     }
3411 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr,
3412                                               gchar const * /*name*/,
3413                                               gchar const * /*old_value*/,
3414                                               gchar const * /*new_value*/,
3415                                               bool /*is_interactive*/,
3416                                               gpointer data)
3418     GtkWidget *tbl = GTK_WIDGET(data);
3420     // quit if run by the attr_changed or selection changed listener
3421     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3422         return;
3423     }
3425     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3426     // SPDocumentUndo::maybe_done() when the document is undo insensitive)
3427     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3429     // TODO: Only update the appropriate part of the toolbar
3430 //    if (!strcmp(name, "inkscape:vp_z")) {
3431         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3432 //    }
3434     Persp3D *persp = persp3d_get_from_repr(repr);
3435     persp3d_update_box_reprs(persp);
3437     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3440 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3442     NULL, /* child_added */
3443     NULL, /* child_removed */
3444     box3d_persp_tb_event_attr_changed,
3445     NULL, /* content_changed */
3446     NULL  /* order_changed */
3447 };
3449 /**
3450  *  \param selection Should not be NULL.
3451  */
3452 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3453 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3454 static void box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3456     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3457     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3458     // update the perspectives with infinite VPs and leave the other ones untouched).
3460     Inkscape::XML::Node *persp_repr = NULL;
3461     purge_repr_listener(tbl, tbl);
3463     SPItem *item = selection->singleItem();
3464     if (item && SP_IS_BOX3D(item)) {
3465         // FIXME: Also deal with multiple selected boxes
3466         SPBox3D *box = SP_BOX3D(item);
3467         Persp3D *persp = box3d_get_perspective(box);
3468         persp_repr = SP_OBJECT_REPR(persp);
3469         if (persp_repr) {
3470             g_object_set_data(tbl, "repr", persp_repr);
3471             Inkscape::GC::anchor(persp_repr);
3472             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3473             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3474         }
3476         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3477         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3478         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3480         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3481         box3d_resync_toolbar(persp_repr, tbl);
3482         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3483     }
3486 static void box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3488     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3489     SPDocument *document = sp_desktop_document(desktop);
3491     // quit if run by the attr_changed or selection changed listener
3492     if (g_object_get_data( dataKludge, "freeze" )) {
3493         return;
3494     }
3496     // in turn, prevent listener from responding
3497     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3499     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3500     if (sel_persps.empty()) {
3501         // this can happen when the document is created; we silently ignore it
3502         return;
3503     }
3504     Persp3D *persp = sel_persps.front();
3506     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3507     SP_OBJECT(persp)->updateRepr();
3509     // TODO: use the correct axis here, too
3510     SPDocumentUndo::maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3512     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3516 static void box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3518     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3521 static void box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3523     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3526 static void box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3528     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3532 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction * /*box3d_angle*/, Proj::Axis axis )
3534     // TODO: Take all selected perspectives into account
3535     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3536     if (sel_persps.empty()) {
3537         // this can happen when the document is created; we silently ignore it
3538         return;
3539     }
3540     Persp3D *persp = sel_persps.front();
3542     bool set_infinite = gtk_toggle_action_get_active(act);
3543     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3546 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3548     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3551 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3553     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3556 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3558     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3561 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3563     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3564     EgeAdjustmentAction* eact = 0;
3565     SPDocument *document = sp_desktop_document (desktop);
3566     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3568     EgeAdjustmentAction* box3d_angle_x = 0;
3569     EgeAdjustmentAction* box3d_angle_y = 0;
3570     EgeAdjustmentAction* box3d_angle_z = 0;
3572     /* Angle X */
3573     {
3574         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3575         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3576         eact = create_adjustment_action( "3DBoxAngleXAction",
3577                                          _("Angle in X direction"), _("Angle X:"),
3578                                          // Translators: PL is short for 'perspective line'
3579                                          _("Angle of PLs in X direction"),
3580                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3581                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3582                                          -360.0, 360.0, 1.0, 10.0,
3583                                          labels, values, G_N_ELEMENTS(labels),
3584                                          box3d_angle_x_value_changed );
3585         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3586         g_object_set_data( holder, "box3d_angle_x_action", eact );
3587         box3d_angle_x = eact;
3588     }
3590     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3591         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3592     } else {
3593         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3594     }
3597     /* VP X state */
3598     {
3599         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3600                                                       // Translators: VP is short for 'vanishing point'
3601                                                       _("State of VP in X direction"),
3602                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3603                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3604                                                       Inkscape::ICON_SIZE_DECORATION );
3605         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3606         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3607         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3608         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3609         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3610     }
3612     /* Angle Y */
3613     {
3614         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3615         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3616         eact = create_adjustment_action( "3DBoxAngleYAction",
3617                                          _("Angle in Y direction"), _("Angle Y:"),
3618                                          // Translators: PL is short for 'perspective line'
3619                                          _("Angle of PLs in Y direction"),
3620                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3621                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3622                                          -360.0, 360.0, 1.0, 10.0,
3623                                          labels, values, G_N_ELEMENTS(labels),
3624                                          box3d_angle_y_value_changed );
3625         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3626         g_object_set_data( holder, "box3d_angle_y_action", eact );
3627         box3d_angle_y = eact;
3628     }
3630     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3631         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3632     } else {
3633         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3634     }
3636     /* VP Y state */
3637     {
3638         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3639                                                       // Translators: VP is short for 'vanishing point'
3640                                                       _("State of VP in Y direction"),
3641                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3642                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3643                                                       Inkscape::ICON_SIZE_DECORATION );
3644         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3645         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3646         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3647         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3648         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3649     }
3651     /* Angle Z */
3652     {
3653         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3654         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3655         eact = create_adjustment_action( "3DBoxAngleZAction",
3656                                          _("Angle in Z direction"), _("Angle Z:"),
3657                                          // Translators: PL is short for 'perspective line'
3658                                          _("Angle of PLs in Z direction"),
3659                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3660                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3661                                          -360.0, 360.0, 1.0, 10.0,
3662                                          labels, values, G_N_ELEMENTS(labels),
3663                                          box3d_angle_z_value_changed );
3664         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3665         g_object_set_data( holder, "box3d_angle_z_action", eact );
3666         box3d_angle_z = eact;
3667     }
3669     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3670         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3671     } else {
3672         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3673     }
3675     /* VP Z state */
3676     {
3677         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3678                                                       // Translators: VP is short for 'vanishing point'
3679                                                       _("State of VP in Z direction"),
3680                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3681                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3682                                                       Inkscape::ICON_SIZE_DECORATION );
3683         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3684         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3685         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3686         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3687         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3688     }
3690     sigc::connection *connection = new sigc::connection(
3691         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3692        );
3693     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3694     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3697 //########################
3698 //##       Spiral       ##
3699 //########################
3701 static void sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3703     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3705     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
3706         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3707         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3708     }
3710     // quit if run by the attr_changed listener
3711     if (g_object_get_data( tbl, "freeze" )) {
3712         return;
3713     }
3715     // in turn, prevent listener from responding
3716     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3718     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3720     bool modmade = false;
3721     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3722          items != NULL;
3723          items = items->next)
3724     {
3725         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3726             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3727             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3728             SP_OBJECT((SPItem *) items->data)->updateRepr();
3729             modmade = true;
3730         }
3731     }
3733     g_free(namespaced_name);
3735     if (modmade) {
3736         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3737                                    _("Change spiral"));
3738     }
3740     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3743 static void sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3745     sp_spl_tb_value_changed(adj, tbl, "revolution");
3748 static void sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3750     sp_spl_tb_value_changed(adj, tbl, "expansion");
3753 static void sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3755     sp_spl_tb_value_changed(adj, tbl, "t0");
3758 static void sp_spl_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
3760     GtkWidget *tbl = GTK_WIDGET(obj);
3762     GtkAdjustment *adj;
3764     // fixme: make settable
3765     gdouble rev = 5;
3766     gdouble exp = 1.0;
3767     gdouble t0 = 0.0;
3769     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3770     gtk_adjustment_set_value(adj, rev);
3771     gtk_adjustment_value_changed(adj);
3773     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3774     gtk_adjustment_set_value(adj, exp);
3775     gtk_adjustment_value_changed(adj);
3777     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3778     gtk_adjustment_set_value(adj, t0);
3779     gtk_adjustment_value_changed(adj);
3781     spinbutton_defocus(GTK_OBJECT(tbl));
3785 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr,
3786                                          gchar const * /*name*/,
3787                                          gchar const * /*old_value*/,
3788                                          gchar const * /*new_value*/,
3789                                          bool /*is_interactive*/,
3790                                          gpointer data)
3792     GtkWidget *tbl = GTK_WIDGET(data);
3794     // quit if run by the _changed callbacks
3795     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3796         return;
3797     }
3799     // in turn, prevent callbacks from responding
3800     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3802     GtkAdjustment *adj;
3803     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3804     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3806     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3807     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3809     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3810     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3812     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3816 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3817     NULL, /* child_added */
3818     NULL, /* child_removed */
3819     spiral_tb_event_attr_changed,
3820     NULL, /* content_changed */
3821     NULL  /* order_changed */
3822 };
3824 static void sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3826     int n_selected = 0;
3827     Inkscape::XML::Node *repr = NULL;
3829     purge_repr_listener( tbl, tbl );
3831     for (GSList const *items = selection->itemList();
3832          items != NULL;
3833          items = items->next)
3834     {
3835         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3836             n_selected++;
3837             repr = SP_OBJECT_REPR((SPItem *) items->data);
3838         }
3839     }
3841     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3843     if (n_selected == 0) {
3844         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3845     } else if (n_selected == 1) {
3846         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3848         if (repr) {
3849             g_object_set_data( tbl, "repr", repr );
3850             Inkscape::GC::anchor(repr);
3851             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3852             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3853         }
3854     } else {
3855         // FIXME: implement averaging of all parameters for multiple selected
3856         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3857         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3858     }
3862 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3864     EgeAdjustmentAction* eact = 0;
3865     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3867     {
3868         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3869         ege_output_action_set_use_markup( act, TRUE );
3870         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3871         g_object_set_data( holder, "mode_action", act );
3872     }
3874     /* Revolution */
3875     {
3876         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3877         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3878         eact = create_adjustment_action( "SpiralRevolutionAction",
3879                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3880                                          "/tools/shapes/spiral/revolution", 3.0,
3881                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3882                                          0.01, 1024.0, 0.1, 1.0,
3883                                          labels, values, G_N_ELEMENTS(labels),
3884                                          sp_spl_tb_revolution_value_changed, 1, 2);
3885         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3886     }
3888     /* Expansion */
3889     {
3890         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3891         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3892         eact = create_adjustment_action( "SpiralExpansionAction",
3893                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3894                                          "/tools/shapes/spiral/expansion", 1.0,
3895                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3896                                          0.0, 1000.0, 0.01, 1.0,
3897                                          labels, values, G_N_ELEMENTS(labels),
3898                                          sp_spl_tb_expansion_value_changed);
3899         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3900     }
3902     /* T0 */
3903     {
3904         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3905         gdouble values[] = {0, 0.5, 0.9};
3906         eact = create_adjustment_action( "SpiralT0Action",
3907                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3908                                          "/tools/shapes/spiral/t0", 0.0,
3909                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3910                                          0.0, 0.999, 0.01, 1.0,
3911                                          labels, values, G_N_ELEMENTS(labels),
3912                                          sp_spl_tb_t0_value_changed);
3913         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3914     }
3916     /* Reset */
3917     {
3918         InkAction* inky = ink_action_new( "SpiralResetAction",
3919                                           _("Defaults"),
3920                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3921                                           GTK_STOCK_CLEAR,
3922                                           secondarySize );
3923         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3924         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3925     }
3928     sigc::connection *connection = new sigc::connection(
3929         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3930         );
3931     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3932     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3935 //########################
3936 //##     Pen/Pencil     ##
3937 //########################
3939 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3940 static Glib::ustring const freehand_tool_name(GObject *dataKludge)
3942     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3943     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3944              ? "/tools/freehand/pen"
3945              : "/tools/freehand/pencil" );
3948 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3950     gint mode = ege_select_one_action_get_active(act);
3952     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3953     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3955     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3957     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3958     // preparatory work here
3959     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3960         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3961         sp_pen_context_set_polyline_mode(pc);
3962     }
3965 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3967     /* Freehand mode toggle buttons */
3968     {
3969         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3970         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3971         Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3973         {
3974             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3976             GtkTreeIter iter;
3977             gtk_list_store_append( model, &iter );
3978             gtk_list_store_set( model, &iter,
3979                                 0, _("Bezier"),
3980                                 1, _("Create regular Bezier path"),
3981                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3982                                 -1 );
3984             gtk_list_store_append( model, &iter );
3985             gtk_list_store_set( model, &iter,
3986                                 0, _("Spiro"),
3987                                 1, _("Create Spiro path"),
3988                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3989                                 -1 );
3991             if (!tool_is_pencil) {
3992                 gtk_list_store_append( model, &iter );
3993                 gtk_list_store_set( model, &iter,
3994                                     0, _("Zigzag"),
3995                                     1, _("Create a sequence of straight line segments"),
3996                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3997                                     -1 );
3999                 gtk_list_store_append( model, &iter );
4000                 gtk_list_store_set( model, &iter,
4001                                     0, _("Paraxial"),
4002                                     1, _("Create a sequence of paraxial line segments"),
4003                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
4004                                     -1 );
4005             }
4007             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
4008                                                                 "FreehandModeActionPencil" :
4009                                                                 "FreehandModeActionPen",
4010                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
4011             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4013             ege_select_one_action_set_appearance( act, "full" );
4014             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4015             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4016             ege_select_one_action_set_icon_column( act, 2 );
4017             ege_select_one_action_set_icon_size( act, secondarySize );
4018             ege_select_one_action_set_tooltip_column( act, 1  );
4020             ege_select_one_action_set_active( act, freehandMode);
4021             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
4022         }
4023     }
4026 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
4027     gint shape = ege_select_one_action_get_active( act );
4028     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4029     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
4032 /**
4033  * \brief Generate the list of freehand advanced shape option entries.
4034  */
4035 static GList * freehand_shape_dropdown_items_list() {
4036     GList *glist = NULL;
4038     glist = g_list_append (glist, _("None"));
4039     glist = g_list_append (glist, _("Triangle in"));
4040     glist = g_list_append (glist, _("Triangle out"));
4041     glist = g_list_append (glist, _("Ellipse"));
4042     glist = g_list_append (glist, _("From clipboard"));
4044     return glist;
4047 static void freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
4049     /*advanced shape options */
4050     {
4051         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4052         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4054         GList* items = 0;
4055         gint count = 0;
4056         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
4057         {
4058             GtkTreeIter iter;
4059             gtk_list_store_append( model, &iter );
4060             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4061             count++;
4062         }
4063         g_list_free( items );
4064         items = 0;
4065         EgeSelectOneAction* act1 = ege_select_one_action_new(
4066             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
4067             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
4068         g_object_set( act1, "short_label", _("Shape:"), NULL );
4069         ege_select_one_action_set_appearance( act1, "compact" );
4070         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
4071         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
4072         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4073         g_object_set_data( holder, "shape_action", act1 );
4074     }
4077 static void sp_pen_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4079     sp_add_freehand_mode_toggle(mainActions, holder, false);
4080     freehand_add_advanced_shape_options(mainActions, holder, false);
4084 static void sp_pencil_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
4086     GtkWidget *tbl = GTK_WIDGET(obj);
4088     GtkAdjustment *adj;
4090     // fixme: make settable
4091     gdouble tolerance = 4;
4093     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
4094     gtk_adjustment_set_value(adj, tolerance);
4095     gtk_adjustment_value_changed(adj);
4097     spinbutton_defocus(GTK_OBJECT(tbl));
4100 static void sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
4102     // quit if run by the attr_changed listener
4103     if (g_object_get_data( tbl, "freeze" )) {
4104         return;
4105     }
4106     // in turn, prevent listener from responding
4107     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4108     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4109     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4110     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4113 /*
4114 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4115 public:
4116     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4117     {
4118         g_object_set_data(_obj, "prefobserver", this);
4119     }
4120     virtual ~PencilToleranceObserver() {
4121         if (g_object_get_data(_obj, "prefobserver") == this) {
4122             g_object_set_data(_obj, "prefobserver", NULL);
4123         }
4124     }
4125     virtual void notify(Inkscape::Preferences::Entry const &val) {
4126         GObject* tbl = _obj;
4127         if (g_object_get_data( tbl, "freeze" )) {
4128             return;
4129         }
4130         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4132         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4134         double v = val.getDouble(adj->value);
4135         gtk_adjustment_set_value(adj, v);
4136         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4137     }
4138 private:
4139     GObject *_obj;
4140 };
4141 */
4143 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4145     sp_add_freehand_mode_toggle(mainActions, holder, true);
4147     EgeAdjustmentAction* eact = 0;
4149     /* Tolerance */
4150     {
4151         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4152         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4153         eact = create_adjustment_action( "PencilToleranceAction",
4154                                          _("Smoothing:"), _("Smoothing: "),
4155                  _("How much smoothing (simplifying) is applied to the line"),
4156                                          "/tools/freehand/pencil/tolerance",
4157                                          3.0,
4158                                          GTK_WIDGET(desktop->canvas), NULL,
4159                                          holder, TRUE, "altx-pencil",
4160                                          1, 100.0, 0.5, 1.0,
4161                                          labels, values, G_N_ELEMENTS(labels),
4162                                          sp_pencil_tb_tolerance_value_changed,
4163                                          1, 2);
4164         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4165         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4166     }
4168     /* advanced shape options */
4169     freehand_add_advanced_shape_options(mainActions, holder, true);
4171     /* Reset */
4172     {
4173         InkAction* inky = ink_action_new( "PencilResetAction",
4174                                           _("Defaults"),
4175                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4176                                           GTK_STOCK_CLEAR,
4177                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4178         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4179         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4180     }
4182     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4187 //########################
4188 //##       Tweak        ##
4189 //########################
4191 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4193     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4194     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4197 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4199     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4200     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4203 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4205     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4206     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4209 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4211     int mode = ege_select_one_action_get_active( act );
4212     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4213     prefs->setInt("/tools/tweak/mode", mode);
4215     static gchar const* names[] = {"tweak_doh", "tweak_dos", "tweak_dol", "tweak_doo", "tweak_channels_label"};
4216     bool flag = ((mode == TWEAK_MODE_COLORPAINT) || (mode == TWEAK_MODE_COLORJITTER));
4217     for (size_t i = 0; i < G_N_ELEMENTS(names); ++i) {
4218         GtkAction *act = GTK_ACTION(g_object_get_data( tbl, names[i] ));
4219         if (act) {
4220             gtk_action_set_sensitive(act, flag);
4221         }
4222     }
4223     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4224     if (fid) {
4225         gtk_action_set_sensitive(fid, !flag);
4226     }
4229 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4231     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4232     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4235 static void tweak_toggle_doh(GtkToggleAction *act, gpointer /*data*/) {
4236     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4237     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4239 static void tweak_toggle_dos(GtkToggleAction *act, gpointer /*data*/) {
4240     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4241     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4243 static void tweak_toggle_dol(GtkToggleAction *act, gpointer /*data*/) {
4244     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4245     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4247 static void tweak_toggle_doo(GtkToggleAction *act, gpointer /*data*/) {
4248     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4249     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4252 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4254     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4255     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4257     {
4258         /* Width */
4259         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4260         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4261         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4262                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4263                                                               "/tools/tweak/width", 15,
4264                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4265                                                               1, 100, 1.0, 10.0,
4266                                                               labels, values, G_N_ELEMENTS(labels),
4267                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4268         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4269         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4270         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4271     }
4274     {
4275         /* Force */
4276         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4277         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4278         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4279                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4280                                                               "/tools/tweak/force", 20,
4281                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4282                                                               1, 100, 1.0, 10.0,
4283                                                               labels, values, G_N_ELEMENTS(labels),
4284                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4285         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4286         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4287         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4288     }
4290     /* Mode */
4291     {
4292         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4294         GtkTreeIter iter;
4295         gtk_list_store_append( model, &iter );
4296         gtk_list_store_set( model, &iter,
4297                             0, _("Move mode"),
4298                             1, _("Move objects in any direction"),
4299                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4300                             -1 );
4302         gtk_list_store_append( model, &iter );
4303         gtk_list_store_set( model, &iter,
4304                             0, _("Move in/out mode"),
4305                             1, _("Move objects towards cursor; with Shift from cursor"),
4306                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4307                             -1 );
4309         gtk_list_store_append( model, &iter );
4310         gtk_list_store_set( model, &iter,
4311                             0, _("Move jitter mode"),
4312                             1, _("Move objects in random directions"),
4313                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4314                             -1 );
4316         gtk_list_store_append( model, &iter );
4317         gtk_list_store_set( model, &iter,
4318                             0, _("Scale mode"),
4319                             1, _("Shrink objects, with Shift enlarge"),
4320                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4321                             -1 );
4323         gtk_list_store_append( model, &iter );
4324         gtk_list_store_set( model, &iter,
4325                             0, _("Rotate mode"),
4326                             1, _("Rotate objects, with Shift counterclockwise"),
4327                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4328                             -1 );
4330         gtk_list_store_append( model, &iter );
4331         gtk_list_store_set( model, &iter,
4332                             0, _("Duplicate/delete mode"),
4333                             1, _("Duplicate objects, with Shift delete"),
4334                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4335                             -1 );
4337         gtk_list_store_append( model, &iter );
4338         gtk_list_store_set( model, &iter,
4339                             0, _("Push mode"),
4340                             1, _("Push parts of paths in any direction"),
4341                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4342                             -1 );
4344         gtk_list_store_append( model, &iter );
4345         gtk_list_store_set( model, &iter,
4346                             0, _("Shrink/grow mode"),
4347                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4348                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4349                             -1 );
4351         gtk_list_store_append( model, &iter );
4352         gtk_list_store_set( model, &iter,
4353                             0, _("Attract/repel mode"),
4354                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4355                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4356                             -1 );
4358         gtk_list_store_append( model, &iter );
4359         gtk_list_store_set( model, &iter,
4360                             0, _("Roughen mode"),
4361                             1, _("Roughen parts of paths"),
4362                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4363                             -1 );
4365         gtk_list_store_append( model, &iter );
4366         gtk_list_store_set( model, &iter,
4367                             0, _("Color paint mode"),
4368                             1, _("Paint the tool's color upon selected objects"),
4369                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4370                             -1 );
4372         gtk_list_store_append( model, &iter );
4373         gtk_list_store_set( model, &iter,
4374                             0, _("Color jitter mode"),
4375                             1, _("Jitter the colors of selected objects"),
4376                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4377                             -1 );
4379         gtk_list_store_append( model, &iter );
4380         gtk_list_store_set( model, &iter,
4381                             0, _("Blur mode"),
4382                             1, _("Blur selected objects more; with Shift, blur less"),
4383                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4384                             -1 );
4387         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4388         g_object_set( act, "short_label", _("Mode:"), NULL );
4389         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4390         g_object_set_data( holder, "mode_action", act );
4392         ege_select_one_action_set_appearance( act, "full" );
4393         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4394         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4395         ege_select_one_action_set_icon_column( act, 2 );
4396         ege_select_one_action_set_icon_size( act, secondarySize );
4397         ege_select_one_action_set_tooltip_column( act, 1  );
4399         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4400         ege_select_one_action_set_active( act, mode );
4401         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4403         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4404     }
4406     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4408     {
4409         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4410         ege_output_action_set_use_markup( act, TRUE );
4411         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4412         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4413             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4414         }
4415         g_object_set_data( holder, "tweak_channels_label", act);
4416     }
4418     {
4419         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4420                                                       _("Hue"),
4421                                                       _("In color mode, act on objects' hue"),
4422                                                       NULL,
4423                                                       Inkscape::ICON_SIZE_DECORATION );
4424         //TRANSLATORS:  "H" here stands for hue
4425         g_object_set( act, "short_label", _("H"), NULL );
4426         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4427         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4428         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4429         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4430             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4431         }
4432         g_object_set_data( holder, "tweak_doh", act);
4433     }
4434     {
4435         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4436                                                       _("Saturation"),
4437                                                       _("In color mode, act on objects' saturation"),
4438                                                       NULL,
4439                                                       Inkscape::ICON_SIZE_DECORATION );
4440         //TRANSLATORS: "S" here stands for Saturation
4441         g_object_set( act, "short_label", _("S"), NULL );
4442         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4443         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4444         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4445         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4446             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4447         }
4448         g_object_set_data( holder, "tweak_dos", act );
4449     }
4450     {
4451         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4452                                                       _("Lightness"),
4453                                                       _("In color mode, act on objects' lightness"),
4454                                                       NULL,
4455                                                       Inkscape::ICON_SIZE_DECORATION );
4456         //TRANSLATORS: "L" here stands for Lightness
4457         g_object_set( act, "short_label", _("L"), NULL );
4458         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4459         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4460         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4461         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4462             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4463         }
4464         g_object_set_data( holder, "tweak_dol", act );
4465     }
4466     {
4467         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4468                                                       _("Opacity"),
4469                                                       _("In color mode, act on objects' opacity"),
4470                                                       NULL,
4471                                                       Inkscape::ICON_SIZE_DECORATION );
4472         //TRANSLATORS: "O" here stands for Opacity
4473         g_object_set( act, "short_label", _("O"), NULL );
4474         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4475         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4476         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4477         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4478             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4479         }
4480         g_object_set_data( holder, "tweak_doo", act );
4481     }
4483     {   /* Fidelity */
4484         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4485         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4486         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4487                                                               _("Fidelity"), _("Fidelity:"),
4488                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4489                                                               "/tools/tweak/fidelity", 50,
4490                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4491                                                               1, 100, 1.0, 10.0,
4492                                                               labels, values, G_N_ELEMENTS(labels),
4493                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4494         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4495         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4496         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4497             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4498         }
4499         g_object_set_data( holder, "tweak_fidelity", eact );
4500     }
4503     /* Use Pressure button */
4504     {
4505         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4506                                                       _("Pressure"),
4507                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4508                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4509                                                       Inkscape::ICON_SIZE_DECORATION );
4510         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4511         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4512         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4513     }
4518 //########################
4519 //##       Spray        ##
4520 //########################
4522 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4524     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4525     prefs->setDouble( "/tools/spray/width", adj->value );
4528 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4530     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4531     prefs->setDouble( "/tools/spray/mean", adj->value );
4534 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4536     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4537     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4540 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4542     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4543     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4546 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject * /*tbl*/ )
4548     int mode = ege_select_one_action_get_active( act );
4549     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4550     prefs->setInt("/tools/spray/mode", mode);
4553 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4555     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4556     prefs->setDouble( "/tools/spray/population", adj->value );
4559 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4561     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4562     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4565 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4567     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4568     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4572 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4574     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4575     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4577     {
4578         /* Width */
4579         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4580         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4581         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4582                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4583                                                               "/tools/spray/width", 15,
4584                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4585                                                               1, 100, 1.0, 10.0,
4586                                                               labels, values, G_N_ELEMENTS(labels),
4587                                                               sp_spray_width_value_changed,  1, 0 );
4588         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4589         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4590         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4591     }
4593     {
4594         /* Mean */
4595         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4596         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4597         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4598                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4599                                                               "/tools/spray/mean", 0,
4600                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4601                                                               0, 100, 1.0, 10.0,
4602                                                               labels, values, G_N_ELEMENTS(labels),
4603                                                               sp_spray_mean_value_changed,  1, 0 );
4604         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4605         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4606         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4607     }
4609     {
4610         /* Standard_deviation */
4611         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4612         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4614         //TRANSLATORS: only translate "string" in "context|string".
4615         // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
4616         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4617                                                               Q_("Toolbox|Scatter"), Q_("Toolbox|Scatter:"), _("Increase to scatter sprayed objects."),
4618                                                               "/tools/spray/standard_deviation", 70,
4619                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4620                                                               1, 100, 1.0, 10.0,
4621                                                               labels, values, G_N_ELEMENTS(labels),
4622                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4623         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4624         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4625         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4626     }
4628     /* Mode */
4629     {
4630         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4632         GtkTreeIter iter;
4633         gtk_list_store_append( model, &iter );
4634         gtk_list_store_set( model, &iter,
4635                             0, _("Spray with copies"),
4636                             1, _("Spray copies of the initial selection"),
4637                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4638                             -1 );
4640         gtk_list_store_append( model, &iter );
4641         gtk_list_store_set( model, &iter,
4642                             0, _("Spray with clones"),
4643                             1, _("Spray clones of the initial selection"),
4644                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4645                             -1 );
4647         gtk_list_store_append( model, &iter );
4648         gtk_list_store_set( model, &iter,
4649                             0, _("Spray single path"),
4650                             1, _("Spray objects in a single path"),
4651                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4652                             -1 );
4654         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4655         g_object_set( act, "short_label", _("Mode:"), NULL );
4656         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4657         g_object_set_data( holder, "mode_action", act );
4659         ege_select_one_action_set_appearance( act, "full" );
4660         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4661         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4662         ege_select_one_action_set_icon_column( act, 2 );
4663         ege_select_one_action_set_icon_size( act, secondarySize );
4664         ege_select_one_action_set_tooltip_column( act, 1  );
4666         gint mode = prefs->getInt("/tools/spray/mode", 1);
4667         ege_select_one_action_set_active( act, mode );
4668         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4670         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4671     }
4673     {   /* Population */
4674         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4675         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4676         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4677                                                               _("Amount"), _("Amount:"),
4678                                                               _("Adjusts the number of items sprayed per clic."),
4679                                                               "/tools/spray/population", 70,
4680                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4681                                                               1, 100, 1.0, 10.0,
4682                                                               labels, values, G_N_ELEMENTS(labels),
4683                                                               sp_spray_population_value_changed,  1, 0 );
4684         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4685         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4686         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4687         g_object_set_data( holder, "spray_population", eact );
4688     }
4690     /* Use Pressure button */
4691     {
4692         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4693                                                       _("Pressure"),
4694                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4695                                                       "use_pressure",
4696                                                       Inkscape::ICON_SIZE_DECORATION );
4697         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4698         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4699         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4700     }
4702     {   /* Rotation */
4703         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4704         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4705         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4706                                                               _("Rotation"), _("Rotation:"),
4707                                                               // xgettext:no-c-format
4708                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4709                                                               "/tools/spray/rotation_variation", 0,
4710                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4711                                                               0, 100, 1.0, 10.0,
4712                                                               labels, values, G_N_ELEMENTS(labels),
4713                                                               sp_spray_rotation_value_changed,  1, 0 );
4714         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4715         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4716         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4717         g_object_set_data( holder, "spray_rotation", eact );
4718     }
4720     {   /* Scale */
4721         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4722         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4724         //TRANSLATORS: only translate "string" in "context|string".
4725         // For more details, see http://developer.gnome.org/doc/API/2.0/glib/glib-I18N.html#Q-:CAPS
4726         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4727                                                               Q_("Toolbox|Scale"), Q_("Toolbox|Scale:"),
4728                                                               // xgettext:no-c-format
4729                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4730                                                               "/tools/spray/scale_variation", 0,
4731                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4732                                                               0, 100, 1.0, 10.0,
4733                                                               labels, values, G_N_ELEMENTS(labels),
4734                                                               sp_spray_scale_value_changed,  1, 0 );
4735         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4736         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4737         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4738         g_object_set_data( holder, "spray_scale", eact );
4739     }
4746 //########################
4747 //##     Calligraphy    ##
4748 //########################
4749 static void update_presets_list(GObject *tbl)
4751     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4752     if (g_object_get_data(tbl, "presets_blocked")) {
4753         return;
4754     }
4756     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4757     if (!sel) {
4758         // WTF!? This will cause a segfault if ever reached
4759         //ege_select_one_action_set_active(sel, 0);
4760         return;
4761     }
4763     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4765     int ege_index = 1;
4766     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4767         bool match = true;
4769         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4770         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4771             Glib::ustring entry_name = j->getEntryName();
4772             if (entry_name == "id" || entry_name == "name") {
4773                 continue;
4774             }
4776             void *widget = g_object_get_data(tbl, entry_name.data());
4777             if (widget) {
4778                 if (GTK_IS_ADJUSTMENT(widget)) {
4779                     double v = j->getDouble();
4780                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4781                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4782                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4783                         match = false;
4784                         break;
4785                     }
4786                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4787                     bool v = j->getBool();
4788                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4789                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4790                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4791                         match = false;
4792                         break;
4793                     }
4794                 }
4795             }
4796         }
4798         if (match) {
4799             // newly added item is at the same index as the
4800             // save command, so we need to change twice for it to take effect
4801             ege_select_one_action_set_active(sel, 0);
4802             ege_select_one_action_set_active(sel, ege_index); // one-based index
4803             return;
4804         }
4805     }
4807     // no match found
4808     ege_select_one_action_set_active(sel, 0);
4811 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4813     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4814     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4815     update_presets_list(tbl);
4818 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4820     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4821     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4822     update_presets_list(tbl);
4825 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4827     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4828     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4829     update_presets_list(tbl);
4832 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4834     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4835     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4836     update_presets_list(tbl);
4839 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4841     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4842     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4843     update_presets_list(tbl);
4846 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4848     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4849     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4850     update_presets_list(tbl);
4853 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4855     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4856     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4857     update_presets_list(tbl);
4860 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4862     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4863     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4864     update_presets_list(tbl);
4867 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4869     // TODO merge into PrefPusher
4870     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4871     if (calligraphy_angle ) {
4872         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4873     }
4877 static gchar const *const widget_names[] = {
4878     "width",
4879     "mass",
4880     "wiggle",
4881     "angle",
4882     "thinning",
4883     "tremor",
4884     "flatness",
4885     "cap_rounding",
4886     "usepressure",
4887     "tracebackground",
4888     "usetilt"
4889 };
4892 static void sp_dcc_build_presets_list(GObject *tbl)
4894     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4896     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4897     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4898     gtk_list_store_clear (model);
4900     {
4901         GtkTreeIter iter;
4902         gtk_list_store_append( model, &iter );
4903         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4904     }
4906     // iterate over all presets to populate the list
4907     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4908     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4909     int ii=1;
4911     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4912         GtkTreeIter iter;
4913         Glib::ustring preset_name = prefs->getString(*i + "/name");
4914         gtk_list_store_append( model, &iter );
4915         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4916     }
4918     {
4919         GtkTreeIter iter;
4920         gtk_list_store_append( model, &iter );
4921         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4922         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4923     }
4925     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4927     update_presets_list (tbl);
4930 static void sp_dcc_save_profile(GtkWidget * /*widget*/, GObject *tbl)
4932     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4933     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4934     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4935     if (! desktop) {
4936         return;
4937     }
4939     if (g_object_get_data(tbl, "presets_blocked")) {
4940         return;
4941     }
4943     CalligraphicProfileRename::show(desktop);
4944     if ( !CalligraphicProfileRename::applied()) {
4945         // dialog cancelled
4946         update_presets_list (tbl);
4947         return;
4948     }
4949     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4951     if (profile_name.empty()) {
4952         // empty name entered
4953         update_presets_list (tbl);
4954         return;
4955     }
4957     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4959     // If there's a preset with the given name, find it and set save_path appropriately
4960     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4961     int total_presets = presets.size();
4962     int new_index = -1;
4963     Glib::ustring save_path; // profile pref path without a trailing slash
4965     int temp_index = 0;
4966     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4967         Glib::ustring name = prefs->getString(*i + "/name");
4968         if (!name.empty() && profile_name == name) {
4969             new_index = temp_index;
4970             save_path = *i;
4971             break;
4972         }
4973     }
4975     if (new_index == -1) {
4976         // no preset with this name, create
4977         new_index = total_presets + 1;
4978         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4979         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4980         g_free(profile_id);
4981     }
4983     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4984         gchar const *const widget_name = widget_names[i];
4985         void *widget = g_object_get_data(tbl, widget_name);
4986         if (widget) {
4987             if (GTK_IS_ADJUSTMENT(widget)) {
4988                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4989                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4990                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4991             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4992                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4993                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4994                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4995             } else {
4996                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4997             }
4998         } else {
4999             g_warning("Bad key when writing preset: %s\n", widget_name);
5000         }
5001     }
5002     prefs->setString(save_path + "/name", profile_name);
5004     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5005     sp_dcc_build_presets_list (tbl);
5009 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl)
5011     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5013     gint preset_index = ege_select_one_action_get_active( act );
5014     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
5015     // even when the preset is not changed. It would be good to replace it with something more
5016     // modern. Index 0 means "No preset", so we don't do anything.
5017     if (preset_index == 0) {
5018         return;
5019     }
5021     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
5023     if (preset_index == save_presets_index) {
5024         // this is the Save command
5025         sp_dcc_save_profile(NULL, tbl);
5026         return;
5027     }
5029     if (g_object_get_data(tbl, "presets_blocked")) {
5030         return;
5031     }
5033     // preset_index is one-based so we subtract 1
5034     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
5035     Glib::ustring preset_path = presets.at(preset_index - 1);
5037     if (!preset_path.empty()) {
5038         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
5040         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
5042         // Shouldn't this be std::map?
5043         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
5044             Glib::ustring entry_name = i->getEntryName();
5045             if (entry_name == "id" || entry_name == "name") {
5046                 continue;
5047             }
5048             void *widget = g_object_get_data(tbl, entry_name.data());
5049             if (widget) {
5050                 if (GTK_IS_ADJUSTMENT(widget)) {
5051                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
5052                     gtk_adjustment_set_value(adj, i->getDouble());
5053                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
5054                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
5055                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5056                     gtk_toggle_action_set_active(toggle, i->getBool());
5057                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
5058                 } else {
5059                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
5060                 }
5061             } else {
5062                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
5063             }
5064         }
5065         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5066     }
5069 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5071     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5072     {
5073         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
5075         EgeAdjustmentAction* calligraphy_angle = 0;
5077         {
5078         /* Width */
5079         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5080         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5081         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
5082                                                               _("Pen Width"), _("Width:"),
5083                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
5084                                                               "/tools/calligraphic/width", 15,
5085                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
5086                                                               1, 100, 1.0, 10.0,
5087                                                               labels, values, G_N_ELEMENTS(labels),
5088                                                               sp_ddc_width_value_changed,  1, 0 );
5089         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5090         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5091         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5092         }
5094         {
5095         /* Thinning */
5096             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
5097             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
5098         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
5099                                                               _("Stroke Thinning"), _("Thinning:"),
5100                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
5101                                                               "/tools/calligraphic/thinning", 10,
5102                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5103                                                               -100, 100, 1, 10.0,
5104                                                               labels, values, G_N_ELEMENTS(labels),
5105                                                               sp_ddc_velthin_value_changed, 1, 0);
5106         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5107         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5108         }
5110         {
5111         /* Angle */
5112         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5113         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5114         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5115                                                               _("Pen Angle"), _("Angle:"),
5116                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5117                                                               "/tools/calligraphic/angle", 30,
5118                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5119                                                               -90.0, 90.0, 1.0, 10.0,
5120                                                               labels, values, G_N_ELEMENTS(labels),
5121                                                               sp_ddc_angle_value_changed, 1, 0 );
5122         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5123         g_object_set_data( holder, "angle_action", eact );
5124         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5125         calligraphy_angle = eact;
5126         }
5128         {
5129         /* Fixation */
5130             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5131         gdouble values[] = {0, 20, 40, 60, 90, 100};
5132         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5133                                                               _("Fixation"), _("Fixation:"),
5134                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5135                                                               "/tools/calligraphic/flatness", 90,
5136                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5137                                                               0.0, 100, 1.0, 10.0,
5138                                                               labels, values, G_N_ELEMENTS(labels),
5139                                                               sp_ddc_flatness_value_changed, 1, 0);
5140         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5141         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5142         }
5144         {
5145         /* Cap Rounding */
5146             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5147         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5148         // TRANSLATORS: "cap" means "end" (both start and finish) here
5149         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5150                                                               _("Cap rounding"), _("Caps:"),
5151                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5152                                                               "/tools/calligraphic/cap_rounding", 0.0,
5153                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5154                                                               0.0, 5.0, 0.01, 0.1,
5155                                                               labels, values, G_N_ELEMENTS(labels),
5156                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5157         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5158         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5159         }
5161         {
5162         /* Tremor */
5163             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5164         gdouble values[] = {0, 10, 20, 40, 60, 100};
5165         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5166                                                               _("Stroke Tremor"), _("Tremor:"),
5167                                                               _("Increase to make strokes rugged and trembling"),
5168                                                               "/tools/calligraphic/tremor", 0.0,
5169                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5170                                                               0.0, 100, 1, 10.0,
5171                                                               labels, values, G_N_ELEMENTS(labels),
5172                                                               sp_ddc_tremor_value_changed, 1, 0);
5174         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5175         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5176         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5177         }
5179         {
5180         /* Wiggle */
5181         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5182         gdouble values[] = {0, 20, 40, 60, 100};
5183         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5184                                                               _("Pen Wiggle"), _("Wiggle:"),
5185                                                               _("Increase to make the pen waver and wiggle"),
5186                                                               "/tools/calligraphic/wiggle", 0.0,
5187                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5188                                                               0.0, 100, 1, 10.0,
5189                                                               labels, values, G_N_ELEMENTS(labels),
5190                                                               sp_ddc_wiggle_value_changed, 1, 0);
5191         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5192         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5193         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5194         }
5196         {
5197         /* Mass */
5198             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5199         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5200         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5201                                                               _("Pen Mass"), _("Mass:"),
5202                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5203                                                               "/tools/calligraphic/mass", 2.0,
5204                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5205                                                               0.0, 100, 1, 10.0,
5206                                                               labels, values, G_N_ELEMENTS(labels),
5207                                                               sp_ddc_mass_value_changed, 1, 0);
5208         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5209         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5210         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5211         }
5214         /* Trace Background button */
5215         {
5216             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5217                                                           _("Trace Background"),
5218                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5219                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5220                                                           Inkscape::ICON_SIZE_DECORATION );
5221             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5222             PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/tracebackground", update_presets_list, holder);
5223             g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5224             g_object_set_data( holder, "tracebackground", act );
5225         }
5227         /* Use Pressure button */
5228         {
5229             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5230                                                           _("Pressure"),
5231                                                           _("Use the pressure of the input device to alter the width of the pen"),
5232                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5233                                                           Inkscape::ICON_SIZE_DECORATION );
5234             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5235             PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usepressure", update_presets_list, holder);
5236             g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5237             g_object_set_data( holder, "usepressure", act );
5238         }
5240         /* Use Tilt button */
5241         {
5242             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5243                                                           _("Tilt"),
5244                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5245                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5246                                                           Inkscape::ICON_SIZE_DECORATION );
5247             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5248             PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usetilt", update_presets_list, holder);
5249             g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5250             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5251             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5252             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5253             g_object_set_data( holder, "usetilt", act );
5254         }
5256         /*calligraphic profile */
5257         {
5258             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5259             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5260             ege_select_one_action_set_appearance (act1, "compact");
5261             g_object_set_data (holder, "profile_selector", act1 );
5263             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5265             sp_dcc_build_presets_list (holder);
5267             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5268             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5269         }
5270     }
5274 //########################
5275 //##    Circle / Arc    ##
5276 //########################
5278 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5280     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5281     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5283     if (v1 == 0 && v2 == 0) {
5284         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5285             gtk_action_set_sensitive( ocb, FALSE );
5286             gtk_action_set_sensitive( make_whole, FALSE );
5287         }
5288     } else {
5289         gtk_action_set_sensitive( ocb, TRUE );
5290         gtk_action_set_sensitive( make_whole, TRUE );
5291     }
5294 static void
5295 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5297     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5299     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
5300         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5301         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5302     }
5304     // quit if run by the attr_changed listener
5305     if (g_object_get_data( tbl, "freeze" )) {
5306         return;
5307     }
5309     // in turn, prevent listener from responding
5310     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5312     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5314     bool modmade = false;
5315     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5316          items != NULL;
5317          items = items->next)
5318     {
5319         SPItem *item = SP_ITEM(items->data);
5321         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5323             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5324             SPArc *arc = SP_ARC(item);
5326             if (!strcmp(value_name, "start")) {
5327                 ge->start = (adj->value * M_PI)/ 180;
5328             } else {
5329                 ge->end = (adj->value * M_PI)/ 180;
5330             }
5332             sp_genericellipse_normalize(ge);
5333             ((SPObject *)arc)->updateRepr();
5334             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5336             modmade = true;
5337         }
5338     }
5340     g_free(namespaced_name);
5342     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5344     sp_arctb_sensitivize( tbl, adj->value, other->value );
5346     if (modmade) {
5347         SPDocumentUndo::maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5348                                    _("Arc: Change start/end"));
5349     }
5351     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5355 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5357     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5360 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5362     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5366 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5368     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5369     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
5370         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5371         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5372     }
5374     // quit if run by the attr_changed listener
5375     if (g_object_get_data( tbl, "freeze" )) {
5376         return;
5377     }
5379     // in turn, prevent listener from responding
5380     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5382     bool modmade = false;
5384     if ( ege_select_one_action_get_active(act) != 0 ) {
5385         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5386              items != NULL;
5387              items = items->next)
5388         {
5389             if (SP_IS_ARC((SPItem *) items->data)) {
5390                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5391                 repr->setAttribute("sodipodi:open", "true");
5392                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5393                 modmade = true;
5394             }
5395         }
5396     } else {
5397         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5398              items != NULL;
5399              items = items->next)
5400         {
5401             if (SP_IS_ARC((SPItem *) items->data)) {
5402                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5403                 repr->setAttribute("sodipodi:open", NULL);
5404                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5405                 modmade = true;
5406             }
5407         }
5408     }
5410     if (modmade) {
5411         SPDocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5412                                    _("Arc: Change open/closed"));
5413     }
5415     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5418 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5420     GtkAdjustment *adj;
5421     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5422     gtk_adjustment_set_value(adj, 0.0);
5423     gtk_adjustment_value_changed(adj);
5425     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5426     gtk_adjustment_set_value(adj, 0.0);
5427     gtk_adjustment_value_changed(adj);
5429     spinbutton_defocus( GTK_OBJECT(obj) );
5432 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const * /*name*/,
5433                                       gchar const * /*old_value*/, gchar const * /*new_value*/,
5434                                       bool /*is_interactive*/, gpointer data)
5436     GObject *tbl = G_OBJECT(data);
5438     // quit if run by the _changed callbacks
5439     if (g_object_get_data( tbl, "freeze" )) {
5440         return;
5441     }
5443     // in turn, prevent callbacks from responding
5444     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5446     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5447     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5449     GtkAdjustment *adj1,*adj2;
5450     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5451     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5452     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5453     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5455     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5457     char const *openstr = NULL;
5458     openstr = repr->attribute("sodipodi:open");
5459     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5461     if (openstr) {
5462         ege_select_one_action_set_active( ocb, 1 );
5463     } else {
5464         ege_select_one_action_set_active( ocb, 0 );
5465     }
5467     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5470 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5471     NULL, /* child_added */
5472     NULL, /* child_removed */
5473     arc_tb_event_attr_changed,
5474     NULL, /* content_changed */
5475     NULL  /* order_changed */
5476 };
5479 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5481     int n_selected = 0;
5482     Inkscape::XML::Node *repr = NULL;
5484     purge_repr_listener( tbl, tbl );
5486     for (GSList const *items = selection->itemList();
5487          items != NULL;
5488          items = items->next)
5489     {
5490         if (SP_IS_ARC((SPItem *) items->data)) {
5491             n_selected++;
5492             repr = SP_OBJECT_REPR((SPItem *) items->data);
5493         }
5494     }
5496     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5498     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5499     if (n_selected == 0) {
5500         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5501     } else if (n_selected == 1) {
5502         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5503         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5505         if (repr) {
5506             g_object_set_data( tbl, "repr", repr );
5507             Inkscape::GC::anchor(repr);
5508             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5509             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5510         }
5511     } else {
5512         // FIXME: implement averaging of all parameters for multiple selected
5513         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5514         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5515         sp_arctb_sensitivize( tbl, 1, 0 );
5516     }
5520 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5522     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5524     EgeAdjustmentAction* eact = 0;
5525     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5528     {
5529         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5530         ege_output_action_set_use_markup( act, TRUE );
5531         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5532         g_object_set_data( holder, "mode_action", act );
5533     }
5535     /* Start */
5536     {
5537         eact = create_adjustment_action( "ArcStartAction",
5538                                          _("Start"), _("Start:"),
5539                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5540                                          "/tools/shapes/arc/start", 0.0,
5541                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5542                                          -360.0, 360.0, 1.0, 10.0,
5543                                          0, 0, 0,
5544                                          sp_arctb_start_value_changed);
5545         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5546     }
5548     /* End */
5549     {
5550         eact = create_adjustment_action( "ArcEndAction",
5551                                          _("End"), _("End:"),
5552                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5553                                          "/tools/shapes/arc/end", 0.0,
5554                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5555                                          -360.0, 360.0, 1.0, 10.0,
5556                                          0, 0, 0,
5557                                          sp_arctb_end_value_changed);
5558         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5559     }
5561     /* Segments / Pie checkbox */
5562     {
5563         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5565         GtkTreeIter iter;
5566         gtk_list_store_append( model, &iter );
5567         gtk_list_store_set( model, &iter,
5568                             0, _("Closed arc"),
5569                             1, _("Switch to segment (closed shape with two radii)"),
5570                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5571                             -1 );
5573         gtk_list_store_append( model, &iter );
5574         gtk_list_store_set( model, &iter,
5575                             0, _("Open Arc"),
5576                             1, _("Switch to arc (unclosed shape)"),
5577                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5578                             -1 );
5580         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5581         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5582         g_object_set_data( holder, "open_action", act );
5584         ege_select_one_action_set_appearance( act, "full" );
5585         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5586         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5587         ege_select_one_action_set_icon_column( act, 2 );
5588         ege_select_one_action_set_icon_size( act, secondarySize );
5589         ege_select_one_action_set_tooltip_column( act, 1  );
5591         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5592         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5593         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5594     }
5596     /* Make Whole */
5597     {
5598         InkAction* inky = ink_action_new( "ArcResetAction",
5599                                           _("Make whole"),
5600                                           _("Make the shape a whole ellipse, not arc or segment"),
5601                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5602                                           secondarySize );
5603         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5604         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5605         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5606         g_object_set_data( holder, "make_whole", inky );
5607     }
5609     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5610     // sensitivize make whole and open checkbox
5611     {
5612         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5613         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5614         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5615     }
5618     sigc::connection *connection = new sigc::connection(
5619         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5620         );
5621     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5622     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5628 // toggle button callbacks and updaters
5630 //########################
5631 //##      Dropper       ##
5632 //########################
5634 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl )
5636     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5637     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5638     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5639     if ( set_action ) {
5640         if ( gtk_toggle_action_get_active( act ) ) {
5641             gtk_action_set_sensitive( set_action, TRUE );
5642         } else {
5643             gtk_action_set_sensitive( set_action, FALSE );
5644         }
5645     }
5647     spinbutton_defocus(GTK_OBJECT(tbl));
5650 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl )
5652     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5653     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5654     spinbutton_defocus(GTK_OBJECT(tbl));
5658 /**
5659  * Dropper auxiliary toolbar construction and setup.
5660  *
5661  * TODO: Would like to add swatch of current color.
5662  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5663  *       can drag and drop places. Will provide a nice mixing palette.
5664  */
5665 static void sp_dropper_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5667     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5668     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5670     {
5671         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5672         ege_output_action_set_use_markup( act, TRUE );
5673         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5674     }
5676     {
5677         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5678                                                       _("Pick opacity"),
5679                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5680                                                       NULL,
5681                                                       Inkscape::ICON_SIZE_DECORATION );
5682         g_object_set( act, "short_label", _("Pick"), NULL );
5683         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5684         g_object_set_data( holder, "pick_action", act );
5685         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5686         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5687     }
5689     {
5690         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5691                                                       _("Assign opacity"),
5692                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5693                                                       NULL,
5694                                                       Inkscape::ICON_SIZE_DECORATION );
5695         g_object_set( act, "short_label", _("Assign"), NULL );
5696         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5697         g_object_set_data( holder, "set_action", act );
5698         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5699         // make sure it's disabled if we're not picking alpha
5700         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5701         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5702     }
5706 //########################
5707 //##      LPETool       ##
5708 //########################
5710 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5712 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5713 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5715     using namespace Inkscape::LivePathEffect;
5717     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5718     SPEventContext *ec = desktop->event_context;
5719     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5720         return;
5721     }
5723     // only take action if run by the attr_changed listener
5724     if (!g_object_get_data(tbl, "freeze")) {
5725         // in turn, prevent listener from responding
5726         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5728         gint mode = ege_select_one_action_get_active(act);
5729         EffectType type = lpesubtools[mode].type;
5731         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5732         bool success = lpetool_try_construction(lc, type);
5733         if (success) {
5734             // since the construction was already performed, we set the state back to inactive
5735             ege_select_one_action_set_active(act, 0);
5736             mode = 0;
5737         } else {
5738             // switch to the chosen subtool
5739             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5740         }
5742         if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
5743             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5744             prefs->setInt( "/tools/lpetool/mode", mode );
5745         }
5747         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5748     }
5751 static void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject * /*tbl*/)
5753     SPEventContext *ec = selection->desktop()->event_context;
5754     if (SP_IS_LPETOOL_CONTEXT(ec)) {
5755         lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5756     }
5759 static void sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5761     using namespace Inkscape::LivePathEffect;
5762     SPEventContext *ec = selection->desktop()->event_context;
5763     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5764         return;
5765     }
5766     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5768     lpetool_delete_measuring_items(lc);
5769     lpetool_create_measuring_items(lc, selection);
5771     // activate line segment combo box if a single item with LPELineSegment is selected
5772     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5773     SPItem *item = selection->singleItem();
5774     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5775         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5776         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5777         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5778             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5779             g_object_set_data(tbl, "currentlpe", lpe);
5780             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5781             gtk_action_set_sensitive(w, TRUE);
5782             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5783         } else {
5784             g_object_set_data(tbl, "currentlpe", NULL);
5785             g_object_set_data(tbl, "currentlpeitem", NULL);
5786             gtk_action_set_sensitive(w, FALSE);
5787         }
5788     } else {
5789         g_object_set_data(tbl, "currentlpe", NULL);
5790         g_object_set_data(tbl, "currentlpeitem", NULL);
5791         gtk_action_set_sensitive(w, FALSE);
5792     }
5795 static void lpetool_toggle_show_bbox(GtkToggleAction *act, gpointer data) {
5796     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5797     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5799     bool show = gtk_toggle_action_get_active( act );
5800     prefs->setBool("/tools/lpetool/show_bbox",  show);
5802     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5803         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5804         lpetool_context_reset_limiting_bbox(lc);
5805     }
5808 static void lpetool_toggle_show_measuring_info(GtkToggleAction *act, GObject *tbl)
5810     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5811     if (!tools_isactive(desktop, TOOLS_LPETOOL)) {
5812         return;
5813     }
5815     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5816     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5817     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5818         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5819         bool show = gtk_toggle_action_get_active( act );
5820         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5821         lpetool_show_measuring_info(lc, show);
5822         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5823     }
5826 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl)
5828     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5829     SPUnit const *unit = tracker->getActiveUnit();
5830     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5831     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5833     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5834     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5835         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5836         lpetool_delete_measuring_items(lc);
5837         lpetool_create_measuring_items(lc);
5838     }
5841 static void lpetool_toggle_set_bbox(GtkToggleAction *act, gpointer data)
5843     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5844     Inkscape::Selection *selection = desktop->selection;
5846     Geom::OptRect bbox = selection->bounds();
5848     if (bbox) {
5849         Geom::Point A(bbox->min());
5850         Geom::Point B(bbox->max());
5852         A *= desktop->doc2dt();
5853         B *= desktop->doc2dt();
5855         // TODO: should we provide a way to store points in prefs?
5856         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5857         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5858         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5859         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5860         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5862         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5863     }
5865     gtk_toggle_action_set_active(act, false);
5868 static void sp_line_segment_build_list(GObject *tbl)
5870     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5872     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5873     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5874     gtk_list_store_clear (model);
5876     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5877     {
5878         GtkTreeIter iter;
5879         gtk_list_store_append( model, &iter );
5880         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5881         gtk_list_store_append( model, &iter );
5882         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5883         gtk_list_store_append( model, &iter );
5884         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5885         gtk_list_store_append( model, &iter );
5886         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5887     }
5889     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5892 static void sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl)
5894     using namespace Inkscape::LivePathEffect;
5896     // quit if run by the attr_changed listener
5897     if (g_object_get_data(tbl, "freeze")) {
5898         return;
5899     }
5901     // in turn, prevent listener from responding
5902     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5904     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5905     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5906     if (lpeitem) {
5907         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5908         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5909         sp_lpe_item_update_patheffect(lpeitem, true, true);
5910     }
5912     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5915 static void lpetool_open_lpe_dialog(GtkToggleAction *act, gpointer data)
5917     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5919     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5920         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5921     }
5922     gtk_toggle_action_set_active(act, false);
5925 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5927     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5928     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5929     g_object_set_data(holder, "tracker", tracker);
5930     SPUnit const *unit = tracker->getActiveUnit();
5932     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5933     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5935     /** Automatically create a list of LPEs that get added to the toolbar **/
5936     {
5937         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5939         GtkTreeIter iter;
5941         // the first toggle button represents the state that no subtool is active (remove this when
5942         // this can be modeled by EgeSelectOneAction or some other action)
5943         gtk_list_store_append( model, &iter );
5944         gtk_list_store_set( model, &iter,
5945                             0, _("All inactive"),
5946                             1, _("No geometric tool is active"),
5947                             2, "draw-geometry-inactive",
5948                             -1 );
5950         Inkscape::LivePathEffect::EffectType type;
5951         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5952             type =  lpesubtools[i].type;
5953             gtk_list_store_append( model, &iter );
5954             gtk_list_store_set( model, &iter,
5955                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5956                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5957                                 2, lpesubtools[i].icon_name,
5958                                 -1 );
5959         }
5961         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5962         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5963         g_object_set_data( holder, "lpetool_mode_action", act );
5965         ege_select_one_action_set_appearance( act, "full" );
5966         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5967         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5968         ege_select_one_action_set_icon_column( act, 2 );
5969         ege_select_one_action_set_tooltip_column( act, 1  );
5971         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5972         ege_select_one_action_set_active( act, lpeToolMode );
5973         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5974     }
5976     /* Show limiting bounding box */
5977     {
5978         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5979                                                       _("Show limiting bounding box"),
5980                                                       _("Show bounding box (used to cut infinite lines)"),
5981                                                       "show-bounding-box",
5982                                                       Inkscape::ICON_SIZE_DECORATION );
5983         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5984         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5985         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5986     }
5988     /* Set limiting bounding box to bbox of current selection */
5989     {
5990         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5991                                                       _("Get limiting bounding box from selection"),
5992                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5993                                                       "draw-geometry-set-bounding-box",
5994                                                       Inkscape::ICON_SIZE_DECORATION );
5995         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5996         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5997         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5998     }
6001     /* Combo box to choose line segment type */
6002     {
6003         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
6004         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
6005         ege_select_one_action_set_appearance (act, "compact");
6006         g_object_set_data (holder, "lpetool_line_segment_action", act );
6008         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
6010         sp_line_segment_build_list (holder);
6012         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
6013         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
6014         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
6015     }
6017     /* Display measuring info for selected items */
6018     {
6019         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
6020                                                       _("Display measuring info"),
6021                                                       _("Display measuring info for selected items"),
6022                                                       "draw-geometry-show-measuring-info",
6023                                                       Inkscape::ICON_SIZE_DECORATION );
6024         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6025         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
6026         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
6027     }
6029     // add the units menu
6030     {
6031         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
6032         gtk_action_group_add_action( mainActions, act );
6033         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
6034         g_object_set_data(holder, "lpetool_units_action", act);
6035         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
6036     }
6038     /* Open LPE dialog (to adapt parameters numerically) */
6039     {
6040         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
6041                                                       _("Open LPE dialog"),
6042                                                       _("Open LPE dialog (to adapt parameters numerically)"),
6043                                                       "dialog-geometry",
6044                                                       Inkscape::ICON_SIZE_DECORATION );
6045         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6046         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
6047         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6048     }
6050     //watch selection
6051     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
6053     sigc::connection *c_selection_modified =
6054         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6055                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
6056     pool->add_connection ("selection-modified", c_selection_modified);
6058     sigc::connection *c_selection_changed =
6059         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6060                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
6061     pool->add_connection ("selection-changed", c_selection_changed);
6064 //########################
6065 //##       Eraser       ##
6066 //########################
6068 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
6070     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6071     prefs->setDouble( "/tools/eraser/width", adj->value );
6072     update_presets_list(tbl);
6075 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6077     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6078     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
6079     if (SPDocumentUndo::get_undo_sensitive(sp_desktop_document(desktop))) {
6080         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6081         prefs->setBool( "/tools/eraser/mode", eraserMode );
6082     }
6084     // only take action if run by the attr_changed listener
6085     if (!g_object_get_data( tbl, "freeze" )) {
6086         // in turn, prevent listener from responding
6087         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6089         if ( eraserMode != 0 ) {
6090         } else {
6091         }
6092         // TODO finish implementation
6094         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6095     }
6098 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6100     {
6101         /* Width */
6102         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
6103         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
6104         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
6105                                                               _("Pen Width"), _("Width:"),
6106                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
6107                                                               "/tools/eraser/width", 15,
6108                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
6109                                                               1, 100, 1.0, 10.0,
6110                                                               labels, values, G_N_ELEMENTS(labels),
6111                                                               sp_erc_width_value_changed, 1, 0);
6112         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6113         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6114         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6115     }
6117     {
6118         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6120         GtkTreeIter iter;
6121         gtk_list_store_append( model, &iter );
6122         gtk_list_store_set( model, &iter,
6123                             0, _("Delete"),
6124                             1, _("Delete objects touched by the eraser"),
6125                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6126                             -1 );
6128         gtk_list_store_append( model, &iter );
6129         gtk_list_store_set( model, &iter,
6130                             0, _("Cut"),
6131                             1, _("Cut out from objects"),
6132                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6133                             -1 );
6135         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6136         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6137         g_object_set_data( holder, "eraser_mode_action", act );
6139         ege_select_one_action_set_appearance( act, "full" );
6140         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6141         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6142         ege_select_one_action_set_icon_column( act, 2 );
6143         ege_select_one_action_set_tooltip_column( act, 1  );
6145         /// @todo Convert to boolean?
6146         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6147         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6148         ege_select_one_action_set_active( act, eraserMode );
6149         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6150     }
6154 //########################
6155 //##    Text Toolbox    ##
6156 //########################
6158 // Functions for debugging:
6159 #ifdef DEBUG_TEXT
6161 static void       sp_print_font( SPStyle *query ) {
6163     bool family_set   = query->text->font_family.set;
6164     bool style_set    = query->font_style.set;
6165     bool fontspec_set = query->text->font_specification.set;
6167     std::cout << "    Family set? " << family_set
6168               << "    Style set? "  << style_set
6169               << "    FontSpec set? " << fontspec_set
6170               << std::endl;
6171     std::cout << "    Family: "
6172               << (query->text->font_family.value ? query->text->font_family.value : "No value")
6173               << "    Style: "    <<  query->font_style.computed
6174               << "    Weight: "   <<  query->font_weight.computed
6175               << "    FontSpec: "
6176               << (query->text->font_specification.value ? query->text->font_specification.value : "No value")
6177               << std::endl;
6179         
6180 static void       sp_print_fontweight( SPStyle *query ) {
6181     const gchar* names[] = {"100", "200", "300", "400", "500", "600", "700", "800", "900",
6182                             "NORMAL", "BOLD", "LIGHTER", "BOLDER", "Out of range"};
6183     // Missing book = 380
6184     int index = query->font_weight.computed;
6185     if( index < 0 || index > 13 ) index = 13;
6186     std::cout << "    Weight: " << names[ index ]
6187               << " (" << query->font_weight.computed << ")" << std::endl;
6191 static void       sp_print_fontstyle( SPStyle *query ) {
6193     const gchar* names[] = {"NORMAL", "ITALIC", "OBLIQUE", "Out of range"};
6194     int index = query->font_style.computed;
6195     if( index < 0 || index > 3 ) index = 3;
6196     std::cout << "    Style:  " << names[ index ] << std::endl;
6199 #endif
6201 // Format family drop-down menu.
6202 static void cell_data_func(GtkCellLayout * /*cell_layout*/,
6203                            GtkCellRenderer   *cell,
6204                            GtkTreeModel      *tree_model,
6205                            GtkTreeIter       *iter,
6206                            gpointer           /*data*/)
6208     gchar *family;
6209     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6210     gchar *const family_escaped = g_markup_escape_text(family, -1);
6212     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6213     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6214     if (show_sample) {
6216         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6217         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6219         std::stringstream markup;
6220         markup << family_escaped << "  <span foreground='gray' font_family='"
6221                << family_escaped << "'>" << sample_escaped << "</span>";
6222         g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6224         g_free(sample_escaped);
6225     } else {
6226         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6227     }
6228     // This doesn't work for two reasons... it set both selected and not selected backgrounds
6229     // to white.. which means that white foreground text is invisible. It also only effects
6230     // the text region, leaving the padding untouched.
6231     // g_object_set (G_OBJECT (cell), "cell-background", "white", "cell-background-set", true, NULL);
6233     g_free(family);
6234     g_free(family_escaped);
6237 // Font family
6238 static void sp_text_fontfamily_value_changed( Ink_ComboBoxEntry_Action *act, GObject *tbl )
6240 #ifdef DEBUG_TEXT
6241     std::cout << std::endl;
6242     std::cout << "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" << std::endl;
6243     std::cout << "sp_text_fontfamily_value_changed: " << std::endl;
6244 #endif
6246      // quit if run by the _changed callbacks
6247     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6248         return;
6249     }
6250     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6252     gchar *family = ink_comboboxentry_action_get_active_text( act );
6253 #ifdef DEBUG_TEXT
6254     std::cout << "  New family: " << family << std::endl;
6255 #endif
6257     // First try to get the old font spec from the stored value
6258     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6259     int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6261     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6263     // If that didn't work, try to get font spec from style
6264     if (fontSpec.empty()) {
6266         // Must query all to fill font-family, font-style, font-weight, font-specification
6267         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6268         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6269         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6271         // Construct a new font specification if it does not yet exist
6272         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6273         if( fontFromStyle ) {
6274             fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6275             fontFromStyle->Unref();
6276         }
6277 #ifdef DEBUG_TEXT
6278         std::cout << "  Fontspec not defined, reconstructed from style :" << fontSpec << ":" << std::endl;
6279         sp_print_font( query );
6280 #endif
6281     }
6283     // And if that didn't work use default
6284     if( fontSpec.empty() ) {
6285         sp_style_read_from_prefs(query, "/tools/text");
6286 #ifdef DEBUG_TEXT
6287         std::cout << "    read style from prefs:" << std::endl;
6288         sp_print_font( query );
6289 #endif
6290         // Construct a new font specification if it does not yet exist
6291         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6292         if( fontFromStyle ) {
6293             fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6294             fontFromStyle->Unref();
6295         }
6296 #ifdef DEBUG_TEXT
6297         std::cout << "  Fontspec not defined, reconstructed from style :" << fontSpec << ":" << std::endl;
6298         sp_print_font( query );
6299 #endif
6300     }
6302     SPCSSAttr *css = sp_repr_css_attr_new ();
6303     if (!fontSpec.empty()) {
6305         // Now we have a font specification, replace family.
6306         Glib::ustring  newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6308 #ifdef DEBUG_TEXT
6309         std::cout << "  New FontSpec from ReplaceFontSpecificationFamily :" << newFontSpec << ":" << std::endl;
6310 #endif
6312         if (!newFontSpec.empty()) {
6314             if (fontSpec != newFontSpec) {
6316                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6318                 if (font) {
6319                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6321                     // Set all the these just in case they were altered when finding the best
6322                     // match for the new family and old style...
6324                     gchar c[256];
6326                     font->Family(c, 256);
6328                     sp_repr_css_set_property (css, "font-family", c);
6330                     font->Attribute( "weight", c, 256);
6331                     sp_repr_css_set_property (css, "font-weight", c);
6333                     font->Attribute("style", c, 256);
6334                     sp_repr_css_set_property (css, "font-style", c);
6336                     font->Attribute("stretch", c, 256);
6337                     sp_repr_css_set_property (css, "font-stretch", c);
6339                     font->Attribute("variant", c, 256);
6340                     sp_repr_css_set_property (css, "font-variant", c);
6342                     font->Unref();
6343                 }
6344             }
6346         } else {
6348             // newFontSpec empty
6349             // If the old font on selection (or default) does not exist on the system,
6350             // or the new font family does not exist,
6351             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6352             // setting the family reported by the family chooser.
6354             // g_print ("fallback setting family: %s\n", family);
6355             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6356             sp_repr_css_set_property (css, "font-family", family);
6357             // Shoud we set other css font attributes?
6358         }
6360     }  // fontSpec not empty or not
6362     // If querying returned nothing, update default style.
6363     if (result_fontspec == QUERY_STYLE_NOTHING)
6364     {
6365         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6366         prefs->mergeStyle("/tools/text/style", css);
6367         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace through a verb
6368     }
6369     else
6370     {
6371         sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true);
6372     }
6374     sp_style_unref(query);
6376     g_free (family);
6378     // Save for undo
6379     SPDocumentUndo::done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6380                                    _("Text: Change font family"));
6381     sp_repr_css_attr_unref (css);
6383     // unfreeze
6384     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6386     // focus to canvas
6387     gtk_widget_grab_focus (GTK_WIDGET((SP_ACTIVE_DESKTOP)->canvas));
6389 #ifdef DEBUG_TEXT
6390     std::cout << "sp_text_toolbox_fontfamily_changes: exit"  << std::endl;
6391     std::cout << "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" << std::endl;
6392     std::cout << std::endl;
6393 #endif
6396 // Font size
6397 static void sp_text_fontsize_value_changed( Ink_ComboBoxEntry_Action *act, GObject *tbl )
6399      // quit if run by the _changed callbacks
6400     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6401         return;
6402     }
6403     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6405     gchar *text = ink_comboboxentry_action_get_active_text( act );
6406     gchar *endptr;
6407     gdouble size = g_strtod( text, &endptr );
6408     if (endptr == text) {  // Conversion failed, non-numeric input.
6409         g_warning( "Conversion of size text to double failed, input: %s\n", text );
6410         g_free( text );
6411         return;
6412     }
6413     g_free( text );
6415     // Set css font size.
6416     SPCSSAttr *css = sp_repr_css_attr_new ();
6417     Inkscape::CSSOStringStream osfs;
6418     osfs << size << "px"; // For now always use px
6419     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6421     // Apply font size to selected objects.
6422     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6423     sp_desktop_set_style (desktop, css, true, true);
6425     // Save for undo
6426     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6427                                    _("Text: Change font size"));
6429     // If no selected objects, set default.
6430     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6431     int result_numbers =
6432         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6433     if (result_numbers == QUERY_STYLE_NOTHING)
6434     {
6435         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6436         prefs->mergeStyle("/tools/text/style", css);
6437     }
6438     sp_style_unref(query);
6440     sp_repr_css_attr_unref (css);
6442     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6445 // Handles both Bold and Italic/Oblique
6446 static void sp_text_style_changed( InkToggleAction* act, GObject *tbl )
6448     // quit if run by the _changed callbacks
6449     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6450         return;
6451     }
6452     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6454     // Called by Bold or Italics button?
6455     const gchar* name = gtk_action_get_name( GTK_ACTION( act ) );
6456     gint prop = (strcmp(name, "TextBoldAction") == 0) ? 0 : 1;
6458     // First query font-specification, this is the most complete font face description.
6459     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6460     int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6462     // font_specification will not be set unless defined explicitely on a tspan.
6463     // This should be fixed!
6464     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6466     if (fontSpec.empty()) {
6467         // Construct a new font specification if it does not yet exist
6468         // Must query font-family, font-style, font-weight, to find correct font face.
6469         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6470         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6471         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6473         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6474         if( fontFromStyle ) {
6475             fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6476             fontFromStyle->Unref();
6477         }
6478     }
6480     // Now that we have the old face, find the new face.
6481     Glib::ustring newFontSpec = "";
6482     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6483     gboolean nochange = true;
6484     gboolean active = gtk_toggle_action_get_active( GTK_TOGGLE_ACTION(act) );
6486     switch (prop)
6487     {
6488         case 0:
6489         {
6490             // Bold
6491             if (!fontSpec.empty()) {
6493                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6495                 if (!newFontSpec.empty()) {
6497                     // Set weight if we found font.
6498                     font_instance * font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6499                     if (font) {
6500                         gchar c[256];
6501                         font->Attribute( "weight", c, 256);
6502                         sp_repr_css_set_property (css, "font-weight", c);
6503                         font->Unref();
6504                         font = NULL;
6505                     }
6506                     nochange = false;
6507                 }
6508             }
6509             // Reset button if no change.
6510             // The reset code didn't work in 0.47 and doesn't here... one must prevent an infinite loop
6511             /*
6512             if(nochange) {
6513                 gtk_action_block_activate( GTK_ACTION(act) );
6514                 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), !active );
6515                 gtk_action_unblock_activate( GTK_ACTION(act) );
6516             }
6517             */
6518             break;
6519         }
6521         case 1:
6522         {
6523             // Italic/Oblique
6524             if (!fontSpec.empty()) {
6526                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6528                 if (!newFontSpec.empty()) {
6530                     // Don't even set the italic/oblique if the font didn't exist on the system
6531                     if( active ) {
6532                         if( newFontSpec.find( "Italic" ) != Glib::ustring::npos ) {
6533                             sp_repr_css_set_property (css, "font-style", "italic");
6534                         } else {
6535                             sp_repr_css_set_property (css, "font-style", "oblique");
6536                         }
6537                     } else {
6538                         sp_repr_css_set_property (css, "font-style", "normal");
6539                     }
6540                     nochange = false;
6541                 }
6542             }
6543             // Reset button if no change.
6544             // The reset code didn't work in 0.47... one must prevent an infinite loop
6545             /*
6546             if(nochange) {
6547                 gtk_action_block_activate( GTK_ACTION(act) );
6548                 gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), !active );
6549                 gtk_action_unblock_activate( GTK_ACTION(act) );
6550             }
6551             */
6552             break;
6553         }
6554     }
6556     if (!newFontSpec.empty()) {
6557         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6558     }
6560     // If querying returned nothing, update default style.
6561     if (result_fontspec == QUERY_STYLE_NOTHING)
6562     {
6563         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6564         prefs->mergeStyle("/tools/text/style", css);
6565     }
6567     sp_style_unref(query);
6569     // Do we need to update other CSS values?
6570     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6571     sp_desktop_set_style (desktop, css, true, true);
6572     SPDocumentUndo::done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6573                                    _("Text: Change font style"));
6574     sp_repr_css_attr_unref (css);
6576     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6579 // Handles both Superscripts and Subscripts
6580 static void sp_text_script_changed( InkToggleAction* act, GObject *tbl )
6582     // quit if run by the _changed callbacks
6583     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6584         return;
6585     }
6586     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6588     // Called by Superscript or Subscript button?
6589     const gchar* name = gtk_action_get_name( GTK_ACTION( act ) );
6590     gint prop = (strcmp(name, "TextSuperscriptAction") == 0) ? 0 : 1;
6592 #ifdef DEBUG_TEXT
6593     std::cout << "sp_text_script_changed: " << prop << std::endl;
6594 #endif
6596     // Query baseline
6597     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6598     int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES);
6600     bool setSuper = false;
6601     bool setSub   = false;
6603     if(result_baseline == QUERY_STYLE_NOTHING || result_baseline == QUERY_STYLE_MULTIPLE_DIFFERENT ) {
6604         // If not set or mixed, turn on superscript or subscript
6605         if( prop == 0 ) {
6606             setSuper = true;
6607         } else {
6608             setSub = true;
6609         }
6610     } else {
6611         // Superscript
6612         gboolean superscriptSet = (query->baseline_shift.set &&
6613                                    query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
6614                                    query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER );
6616         // Subscript
6617         gboolean subscriptSet = (query->baseline_shift.set &&
6618                                  query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
6619                                  query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB );
6621         setSuper = !superscriptSet && prop == 0;
6622         setSub   = !subscriptSet   && prop == 1;
6623     }
6625     // Set css properties
6626     SPCSSAttr *css = sp_repr_css_attr_new ();
6627     if( setSuper || setSub ) {
6628         // Openoffice 2.3 and Adobe use 58%, Microsoft Word 2002 uses 65%, LaTex about 70%.
6629         // 58% looks too small to me, especially if a superscript is placed on a superscript.
6630         // If you make a change here, consider making a change to baseline-shift amount
6631         // in style.cpp.
6632         sp_repr_css_set_property (css, "font-size", "65%");
6633     } else {
6634         sp_repr_css_set_property (css, "font-size", "");
6635     }
6636     if( setSuper ) {
6637         sp_repr_css_set_property (css, "baseline-shift", "super");
6638     } else if( setSub ) {
6639         sp_repr_css_set_property (css, "baseline-shift", "sub");
6640     } else {
6641         sp_repr_css_set_property (css, "baseline-shift", "baseline");
6642     }
6644     // Apply css to selected objects.
6645     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6646     sp_desktop_set_style (desktop, css, true, false);
6648     // Save for undo
6649     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:script", SP_VERB_NONE,
6650                                    _("Text: Change superscript or subscript"));
6652     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6655 static void sp_text_align_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6657     // quit if run by the _changed callbacks
6658     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6659         return;
6660     }
6661     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6663     int mode = ege_select_one_action_get_active( act );
6665     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6666     prefs->setInt("/tools/text/align_mode", mode);
6668     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6670     // move the x of all texts to preserve the same bbox
6671     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6672     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6673         if (SP_IS_TEXT((SPItem *) items->data)) {
6674             SPItem *item = SP_ITEM(items->data);
6676             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6677             // below, variable names suggest horizontal move, but we check the writing direction
6678             // and move in the corresponding axis
6679             int axis;
6680             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6681                 axis = NR::X;
6682             } else {
6683                 axis = NR::Y;
6684             }
6686             Geom::OptRect bbox
6687                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6688             if (!bbox)
6689                 continue;
6690             double width = bbox->dimensions()[axis];
6691             // If you want to align within some frame, other than the text's own bbox, calculate
6692             // the left and right (or top and bottom for tb text) slacks of the text inside that
6693             // frame (currently unused)
6694             double left_slack = 0;
6695             double right_slack = 0;
6696             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6697             double move = 0;
6698             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6699                 switch (mode) {
6700                     case 0:
6701                         move = -left_slack;
6702                         break;
6703                     case 1:
6704                         move = width/2 + (right_slack - left_slack)/2;
6705                         break;
6706                     case 2:
6707                         move = width + right_slack;
6708                         break;
6709                 }
6710             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6711                 switch (mode) {
6712                     case 0:
6713                         move = -width/2 - left_slack;
6714                         break;
6715                     case 1:
6716                         move = (right_slack - left_slack)/2;
6717                         break;
6718                     case 2:
6719                         move = width/2 + right_slack;
6720                         break;
6721                 }
6722             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6723                 switch (mode) {
6724                     case 0:
6725                         move = -width - left_slack;
6726                         break;
6727                     case 1:
6728                         move = -width/2 + (right_slack - left_slack)/2;
6729                         break;
6730                     case 2:
6731                         move = right_slack;
6732                         break;
6733                 }
6734             }
6735             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6736             if (axis == NR::X) {
6737                 XY = XY + Geom::Point (move, 0);
6738             } else {
6739                 XY = XY + Geom::Point (0, move);
6740             }
6741             SP_TEXT(item)->attributes.setFirstXY(XY);
6742             SP_OBJECT(item)->updateRepr();
6743             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6744         }
6745     }
6747     SPCSSAttr *css = sp_repr_css_attr_new ();
6748     switch (mode)
6749     {
6750         case 0:
6751         {
6752             sp_repr_css_set_property (css, "text-anchor", "start");
6753             sp_repr_css_set_property (css, "text-align", "start");
6754             break;
6755         }
6756         case 1:
6757         {
6758             sp_repr_css_set_property (css, "text-anchor", "middle");
6759             sp_repr_css_set_property (css, "text-align", "center");
6760             break;
6761         }
6763         case 2:
6764         {
6765             sp_repr_css_set_property (css, "text-anchor", "end");
6766             sp_repr_css_set_property (css, "text-align", "end");
6767             break;
6768         }
6770         case 3:
6771         {
6772             sp_repr_css_set_property (css, "text-anchor", "start");
6773             sp_repr_css_set_property (css, "text-align", "justify");
6774             break;
6775         }
6776     }
6778     SPStyle *query =
6779         sp_style_new (SP_ACTIVE_DOCUMENT);
6780     int result_numbers =
6781         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6783     // If querying returned nothing, update default style.
6784     if (result_numbers == QUERY_STYLE_NOTHING)
6785     {
6786         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6787         prefs->mergeStyle("/tools/text/style", css);
6788     }
6790     sp_style_unref(query);
6792     sp_desktop_set_style (desktop, css, true, true);
6793     SPDocumentUndo::done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6794                                    _("Text: Change alignment"));
6795     sp_repr_css_attr_unref (css);
6797     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6799     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6802 static void sp_text_lineheight_value_changed( GtkAdjustment *adj, GObject *tbl )
6804     // quit if run by the _changed callbacks
6805     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6806         return;
6807     }
6808     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6810     // At the moment this handles only numerical values (i.e. no percent).
6811     // Set css line height.
6812     SPCSSAttr *css = sp_repr_css_attr_new ();
6813     Inkscape::CSSOStringStream osfs;
6814     osfs << adj->value*100 << "%";
6815     sp_repr_css_set_property (css, "line-height", osfs.str().c_str());
6817     // Apply line-height to selected objects.
6818     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6819     sp_desktop_set_style (desktop, css, true, false);
6822     // Until deprecated sodipodi:linespacing purged:
6823     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6824     GSList const *items = selection->itemList();
6825     for (; items != NULL; items = items->next) {
6826         if (SP_IS_TEXT (items->data)) {
6827             SP_OBJECT_REPR(items->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
6828         }
6829     }
6831     // Save for undo
6832     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:line-height", SP_VERB_NONE,
6833                                    _("Text: Change line-height"));
6835     // If no selected objects, set default.
6836     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6837     int result_numbers =
6838         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6839     if (result_numbers == QUERY_STYLE_NOTHING)
6840     {
6841         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6842         prefs->mergeStyle("/tools/text/style", css);
6843     }
6844     sp_style_unref(query);
6846     sp_repr_css_attr_unref (css);
6848     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6851 static void sp_text_wordspacing_value_changed( GtkAdjustment *adj, GObject *tbl )
6853     // quit if run by the _changed callbacks
6854     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6855         return;
6856     }
6857     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6859     // At the moment this handles only numerical values (i.e. no em unit).
6860     // Set css word-spacing
6861     SPCSSAttr *css = sp_repr_css_attr_new ();
6862     Inkscape::CSSOStringStream osfs;
6863     osfs << adj->value;
6864     sp_repr_css_set_property (css, "word-spacing", osfs.str().c_str());
6866     // Apply word-spacing to selected objects.
6867     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6868     sp_desktop_set_style (desktop, css, true, false);
6870     // Save for undo
6871     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:word-spacing", SP_VERB_NONE,
6872                                    _("Text: Change word-spacing"));
6874     // If no selected objects, set default.
6875     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6876     int result_numbers =
6877         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6878     if (result_numbers == QUERY_STYLE_NOTHING)
6879     {
6880         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6881         prefs->mergeStyle("/tools/text/style", css);
6882     }
6883     sp_style_unref(query);
6885     sp_repr_css_attr_unref (css);
6887     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6890 static void sp_text_letterspacing_value_changed( GtkAdjustment *adj, GObject *tbl )
6892     // quit if run by the _changed callbacks
6893     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6894         return;
6895     }
6896     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6898     // At the moment this handles only numerical values (i.e. no em unit).
6899     // Set css letter-spacing
6900     SPCSSAttr *css = sp_repr_css_attr_new ();
6901     Inkscape::CSSOStringStream osfs;
6902     osfs << adj->value;
6903     sp_repr_css_set_property (css, "letter-spacing", osfs.str().c_str());
6905     // Apply letter-spacing to selected objects.
6906     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6907     sp_desktop_set_style (desktop, css, true, false);
6909     // Save for undo
6910     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:letter-spacing", SP_VERB_NONE,
6911                                    _("Text: Change letter-spacing"));
6913     // If no selected objects, set default.
6914     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
6915     int result_numbers =
6916         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6917     if (result_numbers == QUERY_STYLE_NOTHING)
6918     {
6919         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6920         prefs->mergeStyle("/tools/text/style", css);
6921     }
6922     sp_style_unref(query);
6924     sp_repr_css_attr_unref (css);
6926     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6930 static void sp_text_dx_value_changed( GtkAdjustment *adj, GObject *tbl )
6932     // quit if run by the _changed callbacks
6933     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6934         return;
6935     }
6936     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6938     gdouble new_dx = adj->value;
6940     if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
6941         SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
6942         if( tc ) {
6943             unsigned char_index = -1;
6944             TextTagAttributes *attributes =
6945                 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
6946             if( attributes ) {
6947                 double old_dx = attributes->getDx( char_index );
6948                 double delta_dx = new_dx - old_dx;
6949                 sp_te_adjust_dx( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_dx );
6950             }
6951         }
6952     }
6954     // Save for undo
6955     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:dx", SP_VERB_NONE,
6956                                    _("Text: Change dx (kern)"));
6958     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6961 static void sp_text_dy_value_changed( GtkAdjustment *adj, GObject *tbl )
6963     // quit if run by the _changed callbacks
6964     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6965         return;
6966     }
6967     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6969     gdouble new_dy = adj->value;
6971     if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
6972         SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
6973         if( tc ) {
6974             unsigned char_index = -1;
6975             TextTagAttributes *attributes =
6976                 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
6977             if( attributes ) {
6978                 double old_dy = attributes->getDy( char_index );
6979                 double delta_dy = new_dy - old_dy;
6980                 sp_te_adjust_dy( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_dy );
6981             }
6982         }
6983     }
6985     // Save for undo
6986     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:dy", SP_VERB_NONE,
6987                                    _("Text: Change dy"));
6989     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6992 static void sp_text_rotation_value_changed( GtkAdjustment *adj, GObject *tbl )
6994     // quit if run by the _changed callbacks
6995     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6996         return;
6997     }
6998     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7000     gdouble new_degrees = adj->value;
7002     if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
7003         SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
7004         if( tc ) {
7005             unsigned char_index = -1;
7006             TextTagAttributes *attributes =
7007                 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
7008             if( attributes ) {
7009                 double old_degrees = attributes->getRotate( char_index );
7010                 double delta_deg = new_degrees - old_degrees;
7011                 sp_te_adjust_rotation( tc->text, tc->text_sel_start, tc->text_sel_end, SP_ACTIVE_DESKTOP, delta_deg );
7012             } 
7013         }
7014     }
7016     // Save for undo
7017     SPDocumentUndo::maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:rotate", SP_VERB_NONE,
7018                                    _("Text: Change rotate"));
7020     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7023 static void sp_text_orientation_mode_changed( EgeSelectOneAction *act, GObject *tbl )
7025     // quit if run by the _changed callbacks
7026     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7027         return;
7028     }
7029     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7031     int mode = ege_select_one_action_get_active( act );
7033     SPCSSAttr   *css        = sp_repr_css_attr_new ();
7034     switch (mode)
7035     {
7036         case 0:
7037         {
7038             sp_repr_css_set_property (css, "writing-mode", "lr");
7039             break;
7040         }
7042         case 1:
7043         {
7044             sp_repr_css_set_property (css, "writing-mode", "tb");
7045             break;
7046         }
7047     }
7049     SPStyle *query =
7050         sp_style_new (SP_ACTIVE_DOCUMENT);
7051     int result_numbers =
7052         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
7054     // If querying returned nothing, update default style.
7055     if (result_numbers == QUERY_STYLE_NOTHING)
7056     {
7057         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7058         prefs->mergeStyle("/tools/text/style", css);
7059     }
7061     sp_desktop_set_style (SP_ACTIVE_DESKTOP, css, true, true);
7062     SPDocumentUndo::done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
7063                                    _("Text: Change orientation"));
7064     sp_repr_css_attr_unref (css);
7066     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7069 /*
7070  * This function sets up the text-tool tool-controls, setting the entry boxes
7071  * etc. to the values from the current selection or the default if no selection.
7072  * It is called whenever a text selection is changed, including stepping cursor
7073  * through text.
7074  */
7075 static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/, GObject *tbl)
7077 #ifdef DEBUG_TEXT
7078     static int count = 0;
7079     ++count;
7080     std::cout << std::endl;
7081     std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7082     std::cout << "sp_text_toolbox_selection_changed: start " << count << std::endl;
7084     std::cout << "  Selected items:" << std::endl;
7085     for (GSList const *items = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
7086          items != NULL;
7087          items = items->next)
7088     {
7089         const gchar* id = SP_OBJECT_ID((SPItem *) items->data);
7090         std::cout << "    " << id << std::endl;
7091     }
7092     Glib::ustring selected_text = sp_text_get_selected_text((SP_ACTIVE_DESKTOP)->event_context);
7093     std::cout << "  Selected text:" << std::endl;
7094     std::cout << selected_text << std::endl;
7095 #endif
7097     // quit if run by the _changed callbacks
7098     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7099 #ifdef DEBUG_TEXT
7100         std::cout << "    Frozen, returning" << std::endl;
7101         std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7102         std::cout << std::endl;
7103 #endif
7104         return;
7105     }
7106     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7108     // Only flowed text can be justified, only normal text can be kerned...
7109     // Find out if we have flowed text now so we can use it several places
7110     gboolean isFlow = false;
7111     for (GSList const *items = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
7112          items != NULL;
7113          items = items->next) {
7114         // const gchar* id = SP_OBJECT_ID((SPItem *) items->data);
7115         // std::cout << "    " << id << std::endl;
7116         if( SP_IS_FLOWTEXT(( SPItem *) items->data )) {
7117             isFlow = true;
7118             // std::cout << "   Found flowed text" << std::endl;
7119             break;
7120         }
7121     }
7123     /*
7124      * Query from current selection:
7125      *   Font family (font-family)
7126      *   Style (font-weight, font-style, font-stretch, font-variant, font-align)
7127      *   Numbers (font-size, letter-spacing, word-spacing, line-height, text-anchor, writing-mode)
7128      *   Font specification (Inkscape private attribute)
7129      */
7130     SPStyle *query =
7131         sp_style_new (SP_ACTIVE_DOCUMENT);
7132     int result_family   = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
7133     int result_style    = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
7134     int result_numbers  = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
7135     int result_baseline = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_BASELINES);
7137     // Used later:
7138     sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
7140     /*
7141      * If no text in selection (querying returned nothing), read the style from
7142      * the /tools/text preferencess (default style for new texts). Return if
7143      * tool bar already set to these preferences.
7144      */
7145     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
7146         // There are no texts in selection, read from preferences.
7147         sp_style_read_from_prefs(query, "/tools/text");
7148 #ifdef DEBUG_TEXT
7149         std::cout << "    read style from prefs:" << std::endl;
7150         sp_print_font( query );
7151 #endif
7152         if (g_object_get_data(tbl, "text_style_from_prefs")) {
7153             // Do not reset the toolbar style from prefs if we already did it last time
7154             sp_style_unref(query);
7155             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7156 #ifdef DEBUG_TEXT
7157             std::cout << "    text_style_from_prefs: toolbar already set" << std:: endl;
7158             std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7159             std::cout << std::endl;
7160 #endif
7161             return;
7162         }
7164         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
7165     } else {
7166         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
7167     }
7169     // If we have valid query data for text (font-family, font-specification) set toolbar accordingly.
7170     if (query->text)
7171     {
7172         // Font family
7173         if( query->text->font_family.value ) {
7174             gchar *fontFamily = query->text->font_family.value;
7176             Ink_ComboBoxEntry_Action* fontFamilyAction =
7177                 INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontFamilyAction" ) );
7178             ink_comboboxentry_action_set_active_text( fontFamilyAction, fontFamily );
7179         }
7182         // Size (average of text selected)
7183         double size = query->font_size.computed;
7184         gchar size_text[G_ASCII_DTOSTR_BUF_SIZE];
7185         g_ascii_dtostr (size_text, sizeof (size_text), size);
7187         Ink_ComboBoxEntry_Action* fontSizeAction =
7188             INK_COMBOBOXENTRY_ACTION( g_object_get_data( tbl, "TextFontSizeAction" ) );
7189         ink_comboboxentry_action_set_active_text( fontSizeAction, size_text );
7192         // Weight (Bold)
7193         // Note: in the enumeration, normal and lighter come at the end so we must explicitly test for them.
7194         gboolean boldSet = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) &&
7195                             (query->font_weight.computed != SP_CSS_FONT_WEIGHT_NORMAL) &&
7196                             (query->font_weight.computed != SP_CSS_FONT_WEIGHT_LIGHTER));
7198         InkToggleAction* textBoldAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextBoldAction" ) );
7199         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textBoldAction), boldSet );
7202         // Style (Italic/Oblique)
7203         gboolean italicSet = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
7205         InkToggleAction* textItalicAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextItalicAction" ) );
7206         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textItalicAction), italicSet );
7209         // Superscript
7210         gboolean superscriptSet =
7211             ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) && 
7212              query->baseline_shift.set &&
7213              query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
7214              query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUPER );
7216         InkToggleAction* textSuperscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSuperscriptAction" ) );
7217         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSuperscriptAction), superscriptSet );
7220         // Subscript
7221         gboolean subscriptSet =
7222             ((result_baseline == QUERY_STYLE_SINGLE || result_baseline == QUERY_STYLE_MULTIPLE_SAME ) && 
7223              query->baseline_shift.set &&
7224              query->baseline_shift.type == SP_BASELINE_SHIFT_LITERAL &&
7225              query->baseline_shift.literal == SP_CSS_BASELINE_SHIFT_SUB );
7227         InkToggleAction* textSubscriptAction = INK_TOGGLE_ACTION( g_object_get_data( tbl, "TextSubscriptAction" ) );
7228         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(textSubscriptAction), subscriptSet );
7231         // Alignment
7232         EgeSelectOneAction* textAlignAction = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextAlignAction" ) );
7234         // Note: SVG 1.1 doesn't include text-align, SVG 1.2 Tiny doesn't include text-align="justify"
7235         // text-align="justify" was a draft SVG 1.2 item (along with flowed text).
7236         // Only flowed text can be left and right justified at the same time.
7237         // Disable button if we don't have flowed text.
7239         // The GtkTreeModel class doesn't have a set function so we can't
7240         // simply add an ege_select_one_action_set_sensitive method!
7241         // We must set values directly with the GtkListStore and then
7242         // ask that the GtkAction update the sensitive parameters.
7243         GtkListStore * model = GTK_LIST_STORE( ege_select_one_action_get_model( textAlignAction ) );
7244         GtkTreePath * path = gtk_tree_path_new_from_string("3"); // Justify entry
7245         GtkTreeIter iter;
7246         gtk_tree_model_get_iter( GTK_TREE_MODEL (model), &iter, path );
7247         gtk_list_store_set( model, &iter, /* column */ 3, isFlow, -1 );
7248         ege_select_one_action_update_sensitive( textAlignAction );
7249         // ege_select_one_action_set_sensitive( textAlignAction, 3, isFlow );
7251         int activeButton = 0;
7252         if (query->text_align.computed  == SP_CSS_TEXT_ALIGN_JUSTIFY)
7253         {
7254             activeButton = 3;
7255         } else {
7256             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)  activeButton = 0;
7257             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) activeButton = 1;
7258             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)    activeButton = 2;
7259         }
7260         ege_select_one_action_set_active( textAlignAction, activeButton );
7263         // Line height (spacing)
7264         double height;
7265         if (query->line_height.normal) {
7266             height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
7267         } else {
7268             if (query->line_height.unit == SP_CSS_UNIT_PERCENT) {
7269                 height = query->line_height.value;
7270             } else {
7271                 height = query->line_height.computed;
7272             }
7273         }
7275         GtkAction* lineHeightAction = GTK_ACTION( g_object_get_data( tbl, "TextLineHeightAction" ) );
7276         GtkAdjustment *lineHeightAdjustment =
7277             ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( lineHeightAction ));
7278         gtk_adjustment_set_value( lineHeightAdjustment, height );
7281         // Word spacing
7282         double wordSpacing;
7283         if (query->word_spacing.normal) wordSpacing = 0.0;
7284         else wordSpacing = query->word_spacing.computed; // Assume no units (change in desktop-style.cpp)
7286         GtkAction* wordSpacingAction = GTK_ACTION( g_object_get_data( tbl, "TextWordSpacingAction" ) );
7287         GtkAdjustment *wordSpacingAdjustment =
7288             ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( wordSpacingAction ));
7289         gtk_adjustment_set_value( wordSpacingAdjustment, wordSpacing );
7292         // Letter spacing
7293         double letterSpacing;
7294         if (query->letter_spacing.normal) letterSpacing = 0.0;
7295         else letterSpacing = query->letter_spacing.computed; // Assume no units (change in desktop-style.cpp)
7296  
7297         GtkAction* letterSpacingAction = GTK_ACTION( g_object_get_data( tbl, "TextLetterSpacingAction" ) );
7298         GtkAdjustment *letterSpacingAdjustment =
7299             ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( letterSpacingAction ));
7300         gtk_adjustment_set_value( letterSpacingAdjustment, letterSpacing );
7303         // Orientation
7304         int activeButton2 = (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB ? 0 : 1);
7306         EgeSelectOneAction* textOrientationAction =
7307             EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "TextOrientationAction" ) );
7308         ege_select_one_action_set_active( textOrientationAction, activeButton2 );
7311     } // if( query->text )
7313 #ifdef DEBUG_TEXT
7314     std::cout << "    GUI: fontfamily.value: "
7315               << (query->text->font_family.value ? query->text->font_family.value : "No value")
7316               << std::endl;
7317     std::cout << "    GUI: font_size.computed: "   << query->font_size.computed   << std::endl;
7318     std::cout << "    GUI: font_weight.computed: " << query->font_weight.computed << std::endl;
7319     std::cout << "    GUI: font_style.computed: "  << query->font_style.computed  << std::endl;
7320     std::cout << "    GUI: text_anchor.computed: " << query->text_anchor.computed << std::endl;
7321     std::cout << "    GUI: text_align.computed:  " << query->text_align.computed  << std::endl;
7322     std::cout << "    GUI: line_height.computed: " << query->line_height.computed
7323               << "  line_height.value: "    << query->line_height.value
7324               << "  line_height.unit: "     << query->line_height.unit  << std::endl;
7325     std::cout << "    GUI: word_spacing.computed: " << query->word_spacing.computed
7326               << "  word_spacing.value: "    << query->word_spacing.value
7327               << "  word_spacing.unit: "     << query->word_spacing.unit  << std::endl;
7328     std::cout << "    GUI: letter_spacing.computed: " << query->letter_spacing.computed
7329               << "  letter_spacing.value: "    << query->letter_spacing.value
7330               << "  letter_spacing.unit: "     << query->letter_spacing.unit  << std::endl;
7331     std::cout << "    GUI: writing_mode.computed: " << query->writing_mode.computed << std::endl;
7332 #endif
7334     sp_style_unref(query);
7336     // Kerning (xshift), yshift, rotation.  NB: These are not CSS attributes.
7337     if( SP_IS_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context) ) {
7338         SPTextContext *const tc = SP_TEXT_CONTEXT((SP_ACTIVE_DESKTOP)->event_context);
7339         if( tc ) {
7340             unsigned char_index = -1;
7341             TextTagAttributes *attributes =
7342                 text_tag_attributes_at_position( tc->text, std::min(tc->text_sel_start, tc->text_sel_end), &char_index );
7343             if( attributes ) {
7345                 // Dx
7346                 double dx = attributes->getDx( char_index );
7347                 GtkAction* dxAction = GTK_ACTION( g_object_get_data( tbl, "TextDxAction" ));
7348                 GtkAdjustment *dxAdjustment =
7349                     ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( dxAction ));
7350                 gtk_adjustment_set_value( dxAdjustment, dx );
7352                 // Dy
7353                 double dy = attributes->getDy( char_index );
7354                 GtkAction* dyAction = GTK_ACTION( g_object_get_data( tbl, "TextDyAction" ));
7355                 GtkAdjustment *dyAdjustment =
7356                     ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( dyAction ));
7357                 gtk_adjustment_set_value( dyAdjustment, dy );
7359                 // Rotation
7360                 double rotation = attributes->getRotate( char_index );
7361                 /* SVG value is between 0 and 360 but we're using -180 to 180 in widget */
7362                 if( rotation > 180.0 ) rotation -= 360.0;
7363                 GtkAction* rotationAction = GTK_ACTION( g_object_get_data( tbl, "TextRotationAction" ));
7364                 GtkAdjustment *rotationAdjustment =
7365                     ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION( rotationAction ));
7366                 gtk_adjustment_set_value( rotationAdjustment, rotation );
7368 #ifdef DEBUG_TEXT
7369                 std::cout << "    GUI: Dx: " << dx << std::endl;
7370                 std::cout << "    GUI: Dy: " << dy << std::endl;
7371                 std::cout << "    GUI: Rotation: " << rotation << std::endl;
7372 #endif
7373             }
7374         }
7375     }
7377     {
7378         // Set these here as we don't always have kerning/rotating attributes
7379         GtkAction* dxAction = GTK_ACTION( g_object_get_data( tbl, "TextDxAction" ));
7380         gtk_action_set_sensitive( GTK_ACTION(dxAction), !isFlow );
7382         GtkAction* dyAction = GTK_ACTION( g_object_get_data( tbl, "TextDyAction" ));
7383         gtk_action_set_sensitive( GTK_ACTION(dyAction), !isFlow );
7385         GtkAction* rotationAction = GTK_ACTION( g_object_get_data( tbl, "TextRotationAction" ));
7386         gtk_action_set_sensitive( GTK_ACTION(rotationAction), !isFlow );
7387     }
7389 #ifdef DEBUG_TEXT
7390     std::cout << "&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&" << std::endl;
7391     std::cout << std::endl;
7392 #endif
7394     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7398 static void sp_text_toolbox_selection_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
7400     sp_text_toolbox_selection_changed (selection, tbl);
7403 void
7404 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
7406     sp_text_toolbox_selection_changed (NULL, tbl);
7409 // Define all the "widgets" in the toolbar.
7410 static void sp_text_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7412     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7413     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7415     // Is this used?
7416     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7417     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
7418     g_object_set_data( holder, "tracker", tracker );
7420     /* Font family */
7421     {
7422         // Font list
7423         Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
7424         GtkListStore* model = store->gobj();
7426         Ink_ComboBoxEntry_Action* act = ink_comboboxentry_action_new( "TextFontFamilyAction",
7427                                                                       _("Font Family"),
7428                                                                       _("Select Font Family (Alt-X to access)"),
7429                                                                       NULL,
7430                                                                       GTK_TREE_MODEL(model),
7431                                                                       -1,                // Entry width
7432                                                                       50,                // Extra list width
7433                                                                       (gpointer)cell_data_func ); // Cell layout
7434         ink_comboboxentry_action_popup_enable( act ); // Enable entry completion
7435         gchar *const warning = _("Font not found on system");
7436         ink_comboboxentry_action_set_warning( act, warning ); // Show icon with tooltip if missing font
7437         ink_comboboxentry_action_set_altx_name( act, "altx-text" ); // Set Alt-X keyboard shortcut
7438         g_signal_connect( G_OBJECT(act), "changed", G_CALLBACK(sp_text_fontfamily_value_changed), holder );
7439         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7440         g_object_set_data( holder, "TextFontFamilyAction", act );
7442         // Change style of drop-down from menu to list
7443         gtk_rc_parse_string (
7444             "style \"dropdown-as-list-style\"\n"
7445             "{\n"
7446             "    GtkComboBox::appears-as-list = 1\n"
7447             "}\n"
7448             "widget \"*.TextFontFamilyAction_combobox\" style \"dropdown-as-list-style\"");
7449     }
7451     /* Font size */
7452     {
7453         // List of font sizes for drop-down menu
7454         GtkListStore* model_size = gtk_list_store_new( 1, G_TYPE_STRING );
7455         gchar const *const sizes[] = {
7456             "4",  "6",  "8",  "9", "10", "11", "12", "13", "14", "16",
7457             "18", "20", "22", "24", "28", "32", "36", "40", "48", "56",
7458             "64", "72", "144"
7459         };
7460         for( unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i ) {
7461             GtkTreeIter iter;
7462             gtk_list_store_append( model_size, &iter );
7463             gtk_list_store_set( model_size, &iter, 0, sizes[i], -1 );
7464         }
7466         Ink_ComboBoxEntry_Action* act = ink_comboboxentry_action_new( "TextFontSizeAction",
7467                                                                       _("Font Size"),
7468                                                                       _("Font size (px)"),
7469                                                                       NULL,
7470                                                                       GTK_TREE_MODEL(model_size),
7471                                                                       4 ); // Width in characters
7472         g_signal_connect( G_OBJECT(act), "changed", G_CALLBACK(sp_text_fontsize_value_changed), holder );
7473         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7474         g_object_set_data( holder, "TextFontSizeAction", act );
7475     }
7477     /* Style - Bold */
7478     {
7479         InkToggleAction* act = ink_toggle_action_new( "TextBoldAction",               // Name
7480                                                       _("Toggle Bold"),               // Label
7481                                                       _("Toggle bold or normal weight"),  // Tooltip
7482                                                       GTK_STOCK_BOLD,                 // Icon (inkId)
7483                                                       secondarySize );                // Icon size
7484         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7485         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_style_changed), holder );
7486         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/bold", false) );
7487         g_object_set_data( holder, "TextBoldAction", act );
7488     }
7490     /* Style - Italic/Oblique */
7491     {
7492         InkToggleAction* act = ink_toggle_action_new( "TextItalicAction",                     // Name
7493                                                       _("Toggle Italic/Oblique"),             // Label
7494                                                       _("Toggle italic/oblique style"),// Tooltip
7495                                                       GTK_STOCK_ITALIC,                       // Icon (inkId)
7496                                                       secondarySize );                        // Icon size
7497         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7498         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_style_changed), holder );
7499         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/italic", false) );
7500         g_object_set_data( holder, "TextItalicAction", act );
7501     }
7503     /* Style - Superscript */
7504     {
7505         InkToggleAction* act = ink_toggle_action_new( "TextSuperscriptAction",             // Name
7506                                                       _("Toggle Superscript"),             // Label
7507                                                       _("Toggle superscript"),             // Tooltip
7508                                                       "text_superscript",                  // Icon (inkId)
7509                                                       secondarySize );                     // Icon size
7510         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7511         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder );
7512         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/super", false) );
7513         g_object_set_data( holder, "TextSuperscriptAction", act );
7514     }
7516     /* Style - Subscript */
7517     {
7518         InkToggleAction* act = ink_toggle_action_new( "TextSubscriptAction",             // Name
7519                                                       _("Toggle Subscript"),             // Label
7520                                                       _("Toggle subscript"),             // Tooltip
7521                                                       "text_subscript",                  // Icon (inkId)
7522                                                       secondarySize );                     // Icon size
7523         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7524         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_text_script_changed), holder );
7525         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/text/sub", false) );
7526         g_object_set_data( holder, "TextSubscriptAction", act );
7527     }
7529     /* Alignment */
7530     {
7531         GtkListStore* model = gtk_list_store_new( 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
7533         GtkTreeIter iter;
7535         gtk_list_store_append( model, &iter );
7536         gtk_list_store_set( model, &iter,
7537                             0, _("Align left"),
7538                             1, _("Align left"),
7539                             2, GTK_STOCK_JUSTIFY_LEFT,
7540                             3, true,
7541                             -1 );
7543         gtk_list_store_append( model, &iter );
7544         gtk_list_store_set( model, &iter,
7545                             0, _("Align center"),
7546                             1, _("Align center"),
7547                             2, GTK_STOCK_JUSTIFY_CENTER,
7548                             3, true,
7549                             -1 );
7551         gtk_list_store_append( model, &iter );
7552         gtk_list_store_set( model, &iter,
7553                             0, _("Align right"),
7554                             1, _("Align right"),
7555                             2, GTK_STOCK_JUSTIFY_RIGHT,
7556                             3, true,
7557                             -1 );
7559         gtk_list_store_append( model, &iter );
7560         gtk_list_store_set( model, &iter,
7561                             0, _("Justify"),
7562                             1, _("Justify (only flowed text)"),
7563                             2, GTK_STOCK_JUSTIFY_FILL,
7564                             3, false,
7565                             -1 );
7567         EgeSelectOneAction* act = ege_select_one_action_new( "TextAlignAction",       // Name
7568                                                              _("Alignment"),          // Label
7569                                                              _("Text alignment"),     // Tooltip
7570                                                              NULL,                    // StockID
7571                                                              GTK_TREE_MODEL(model) ); // Model
7572         g_object_set( act, "short_label", "NotUsed", NULL );
7573         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7574         g_object_set_data( holder, "TextAlignAction", act );
7576         ege_select_one_action_set_appearance( act, "full" );
7577         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
7578         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
7579         ege_select_one_action_set_icon_column( act, 2 );
7580         ege_select_one_action_set_icon_size( act, secondarySize );
7581         ege_select_one_action_set_tooltip_column( act, 1  );
7582         ege_select_one_action_set_sensitive_column( act, 3 );
7583         gint mode = prefs->getInt("/tools/text/align_mode", 0);
7584         ege_select_one_action_set_active( act, mode );
7585         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_align_mode_changed), holder );
7586     }
7588     /* Orientation (Left to Right, Top to Bottom */
7589     {
7590         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
7592         GtkTreeIter iter;
7594         gtk_list_store_append( model, &iter );
7595         gtk_list_store_set( model, &iter,
7596                             0, _("Horizontal"),
7597                             1, _("Horizontal text"),
7598                             2, INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL,
7599                             -1 );
7601         gtk_list_store_append( model, &iter );
7602         gtk_list_store_set( model, &iter,
7603                             0, _("Vertical"),
7604                             1, _("Vertical text"),
7605                             2, INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL,
7606                             -1 );
7608         EgeSelectOneAction* act = ege_select_one_action_new( "TextOrientationAction", // Name
7609                                                              _("Orientation"),        // Label
7610                                                              _("Text orientation"),   // Tooltip
7611                                                              NULL,                    // StockID
7612                                                              GTK_TREE_MODEL(model) ); // Model
7614         g_object_set( act, "short_label", "NotUsed", NULL );
7615         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
7616         g_object_set_data( holder, "TextOrientationAction", act );
7618         ege_select_one_action_set_appearance( act, "full" );
7619         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
7620         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
7621         ege_select_one_action_set_icon_column( act, 2 );
7622         ege_select_one_action_set_icon_size( act, secondarySize );
7623         ege_select_one_action_set_tooltip_column( act, 1  );
7625         gint mode = prefs->getInt("/tools/text/orientation", 0);
7626         ege_select_one_action_set_active( act, mode );
7627         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_text_orientation_mode_changed), holder );
7628     }
7630     /* Line height */
7631     {
7632         // Drop down menu
7633         gchar const* labels[] = {_("Smaller spacing"), 0, 0, 0, 0, _("Normal"), 0, 0, 0, 0, 0, _("Larger spacing")};
7634         gdouble values[] = { 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1,2, 1.3, 1.4, 1.5, 2.0};
7636         EgeAdjustmentAction *eact = create_adjustment_action(
7637             "TextLineHeightAction",               /* name */
7638             _("Line Height"),                     /* label */
7639             _("Line:"),                           /* short label */
7640             _("Spacing between lines (times font size)"),      /* tooltip */
7641             "/tools/text/lineheight",             /* preferences path */
7642             0.0,                                  /* default */
7643             GTK_WIDGET(desktop->canvas),          /* focusTarget */
7644             NULL,                                 /* unit selector */
7645             holder,                               /* dataKludge */
7646             FALSE,                                /* set alt-x keyboard shortcut? */
7647             NULL,                                 /* altx_mark */
7648             0.0, 10.0, 0.01, 0.10,                /* lower, upper, step (arrow up/down), page up/down */
7649             labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7650             sp_text_lineheight_value_changed,     /* callback */
7651             0.1,                                  /* step (used?) */
7652             2,                                    /* digits to show */
7653             1.0                                   /* factor (multiplies default) */
7654             );
7655         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7656         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7657         g_object_set_data( holder, "TextLineHeightAction", eact );
7658         g_object_set( G_OBJECT(eact), "iconId", "text_line_spacing", NULL );
7659     }
7661     /* Word spacing */
7662     {
7663         // Drop down menu
7664         gchar const* labels[] = {_("Negative spacing"), 0, 0, 0, _("Normal"), 0, 0, 0, 0, 0, 0, 0, _("Positive spacing")};
7665         gdouble values[] = {-2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0};
7667         EgeAdjustmentAction *eact = create_adjustment_action(
7668             "TextWordSpacingAction",              /* name */
7669             _("Word spacing"),                    /* label */
7670             _("Word:"),                           /* short label */
7671             _("Spacing between words (px)"),     /* tooltip */
7672             "/tools/text/wordspacing",            /* preferences path */
7673             0.0,                                  /* default */
7674             GTK_WIDGET(desktop->canvas),          /* focusTarget */
7675             NULL,                                 /* unit selector */
7676             holder,                               /* dataKludge */
7677             FALSE,                                /* set alt-x keyboard shortcut? */
7678             NULL,                                 /* altx_mark */
7679             -100.0, 100.0, 0.01, 0.10,            /* lower, upper, step (arrow up/down), page up/down */
7680             labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7681             sp_text_wordspacing_value_changed,    /* callback */
7682             0.1,                                  /* step (used?) */
7683             2,                                    /* digits to show */
7684             1.0                                   /* factor (multiplies default) */
7685             );
7686         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7687         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7688         g_object_set_data( holder, "TextWordSpacingAction", eact );
7689         g_object_set( G_OBJECT(eact), "iconId", "text_word_spacing", NULL );
7690     }
7692     /* Letter spacing */
7693     {
7694         // Drop down menu
7695         gchar const* labels[] = {_("Negative spacing"), 0, 0, 0, _("Normal"), 0, 0, 0, 0, 0, 0, 0, _("Positive spacing")};
7696         gdouble values[] = {-2.0, -1.5, -1.0, -0.5, 0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0};
7698         EgeAdjustmentAction *eact = create_adjustment_action(
7699             "TextLetterSpacingAction",            /* name */
7700             _("Letter spacing"),                  /* label */
7701             _("Letter:"),                         /* short label */
7702             _("Spacing between letters (px)"),   /* tooltip */
7703             "/tools/text/letterspacing",          /* preferences path */
7704             0.0,                                  /* default */
7705             GTK_WIDGET(desktop->canvas),          /* focusTarget */
7706             NULL,                                 /* unit selector */
7707             holder,                               /* dataKludge */
7708             FALSE,                                /* set alt-x keyboard shortcut? */
7709             NULL,                                 /* altx_mark */
7710             -100.0, 100.0, 0.01, 0.10,            /* lower, upper, step (arrow up/down), page up/down */
7711             labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7712             sp_text_letterspacing_value_changed,  /* callback */
7713             0.1,                                  /* step (used?) */
7714             2,                                    /* digits to show */
7715             1.0                                   /* factor (multiplies default) */
7716             );
7717         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7718         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7719         g_object_set_data( holder, "TextLetterSpacingAction", eact );
7720         g_object_set( G_OBJECT(eact), "iconId", "text_letter_spacing", NULL );
7721     }
7723     /* Character kerning (horizontal shift) */
7724     {
7725         // Drop down menu
7726         gchar const* labels[] = {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 };
7727         gdouble values[]      = { -2.0, -1.5, -1.0, -0.5,   0,  0.5,  1.0,  1.5,  2.0, 2.5 };
7729         EgeAdjustmentAction *eact = create_adjustment_action(
7730             "TextDxAction",                       /* name */
7731             _("Kerning"),                         /* label */
7732             _("Kern:"),                           /* short label */
7733             _("Horizontal kerning (px)"), /* tooltip */
7734             "/tools/text/dx",                     /* preferences path */
7735             0.0,                                  /* default */
7736             GTK_WIDGET(desktop->canvas),          /* focusTarget */
7737             NULL,                                 /* unit selector */
7738             holder,                               /* dataKludge */
7739             FALSE,                                /* set alt-x keyboard shortcut? */
7740             NULL,                                 /* altx_mark */
7741             -100.0, 100.0, 0.01, 0.1,             /* lower, upper, step (arrow up/down), page up/down */
7742             labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7743             sp_text_dx_value_changed,             /* callback */
7744             0.1,                                  /* step (used?) */
7745             2,                                    /* digits to show */
7746             1.0                                   /* factor (multiplies default) */
7747             );
7748         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7749         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7750         g_object_set_data( holder, "TextDxAction", eact );
7751         g_object_set( G_OBJECT(eact), "iconId", "text_horz_kern", NULL );
7752     }
7754     /* Character vertical shift */
7755     {
7756         // Drop down menu
7757         gchar const* labels[] = {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 };
7758         gdouble values[]      = { -2.0, -1.5, -1.0, -0.5,   0,  0.5,  1.0,  1.5,  2.0, 2.5 };
7760         EgeAdjustmentAction *eact = create_adjustment_action(
7761             "TextDyAction",                       /* name */
7762             _("Vertical Shift"),                  /* label */
7763             _("Vert:"),                           /* short label */
7764             _("Vertical shift (px)"),   /* tooltip */
7765             "/tools/text/dy",                     /* preferences path */
7766             0.0,                                  /* default */
7767             GTK_WIDGET(desktop->canvas),          /* focusTarget */
7768             NULL,                                 /* unit selector */
7769             holder,                               /* dataKludge */
7770             FALSE,                                /* set alt-x keyboard shortcut? */
7771             NULL,                                 /* altx_mark */
7772             -100.0, 100.0, 0.01, 0.1,             /* lower, upper, step (arrow up/down), page up/down */
7773             labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7774             sp_text_dy_value_changed,             /* callback */
7775             0.1,                                  /* step (used?) */
7776             2,                                    /* digits to show */
7777             1.0                                   /* factor (multiplies default) */
7778             );
7779         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7780         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7781         g_object_set_data( holder, "TextDyAction", eact );
7782         g_object_set( G_OBJECT(eact), "iconId", "text_vert_kern", NULL );
7783     }
7785     /* Character rotation */
7786     {
7787         // Drop down menu
7788         gchar const* labels[] = {   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 };
7789         gdouble values[]      = { -90, -45, -30, -15,   0,  15,  30,  45,  90, 180 };
7791         EgeAdjustmentAction *eact = create_adjustment_action(
7792             "TextRotationAction",                 /* name */
7793             _("Letter rotation"),                 /* label */
7794             _("Rot:"),                            /* short label */
7795             _("Character rotation (degrees)"),/* tooltip */
7796             "/tools/text/rotation",               /* preferences path */
7797             0.0,                                  /* default */
7798             GTK_WIDGET(desktop->canvas),          /* focusTarget */
7799             NULL,                                 /* unit selector */
7800             holder,                               /* dataKludge */
7801             FALSE,                                /* set alt-x keyboard shortcut? */
7802             NULL,                                 /* altx_mark */
7803             -180.0, 180.0, 0.1, 1.0,              /* lower, upper, step (arrow up/down), page up/down */
7804             labels, values, G_N_ELEMENTS(labels), /* drop down menu */
7805             sp_text_rotation_value_changed,       /* callback */
7806             0.1,                                  /* step (used?) */
7807             2,                                    /* digits to show */
7808             1.0                                   /* factor (multiplies default) */
7809             );
7810         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7811         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
7812         g_object_set_data( holder, "TextRotationAction", eact );
7813         g_object_set( G_OBJECT(eact), "iconId", "text_rotation", NULL );
7814     }
7816     // Is this necessary to call? Shouldn't hurt.
7817     sp_text_toolbox_selection_changed(sp_desktop_selection(desktop), holder);
7819     // Watch selection
7820     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolboxGTK");
7822     sigc::connection *c_selection_changed =
7823         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7824                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)holder)));
7825     pool->add_connection ("selection-changed", c_selection_changed);
7827     sigc::connection *c_selection_modified =
7828         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7829                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)holder)));
7830     pool->add_connection ("selection-modified", c_selection_modified);
7832     sigc::connection *c_subselection_changed =
7833         new sigc::connection (desktop->connectToolSubselectionChanged
7834                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)holder)));
7835     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7837     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
7839     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
7844 //#########################
7845 //##      Connector      ##
7846 //#########################
7848 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7850     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7851     prefs->setBool("/tools/connector/mode",
7852                 gtk_toggle_action_get_active( act ));
7855 static void sp_connector_path_set_avoid(void)
7857     cc_selection_set_avoid(true);
7861 static void sp_connector_path_set_ignore(void)
7863     cc_selection_set_avoid(false);
7866 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7868     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7869     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7870     SPDocument *doc = sp_desktop_document(desktop);
7872     if (!SPDocumentUndo::get_undo_sensitive(doc)) {
7873         return;
7874     }
7877     // quit if run by the _changed callbacks
7878     if (g_object_get_data( tbl, "freeze" )) {
7879         return;
7880     }
7882     // in turn, prevent callbacks from responding
7883     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7885     bool is_orthog = gtk_toggle_action_get_active( act );
7886     gchar orthog_str[] = "orthogonal";
7887     gchar polyline_str[] = "polyline";
7888     gchar *value = is_orthog ? orthog_str : polyline_str ;
7890     bool modmade = false;
7891     GSList *l = (GSList *) selection->itemList();
7892     while (l) {
7893         SPItem *item = (SPItem *) l->data;
7895         if (cc_item_is_connector(item)) {
7896             item->setAttribute( "inkscape:connector-type",
7897                     value, false);
7898             item->avoidRef->handleSettingChange();
7899             modmade = true;
7900         }
7901         l = l->next;
7902     }
7904     if (!modmade) {
7905         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7906         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7907     }
7909     SPDocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR,
7910             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7912     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7915 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7917     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7918     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7919     SPDocument *doc = sp_desktop_document(desktop);
7921     if (!SPDocumentUndo::get_undo_sensitive(doc)) {
7922         return;
7923     }
7926     // quit if run by the _changed callbacks
7927     if (g_object_get_data( tbl, "freeze" )) {
7928         return;
7929     }
7931     // in turn, prevent callbacks from responding
7932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7934     gdouble newValue = gtk_adjustment_get_value(adj);
7935     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7936     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7938     bool modmade = false;
7939     GSList *l = (GSList *) selection->itemList();
7940     while (l) {
7941         SPItem *item = (SPItem *) l->data;
7943         if (cc_item_is_connector(item)) {
7944             item->setAttribute( "inkscape:connector-curvature",
7945                     value, false);
7946             item->avoidRef->handleSettingChange();
7947             modmade = true;
7948         }
7949         l = l->next;
7950     }
7952     if (!modmade) {
7953         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7954         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7955     }
7957     SPDocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR,
7958             _("Change connector curvature"));
7960     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7964 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7966     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7967     SPDocument *doc = sp_desktop_document(desktop);
7969     if (!SPDocumentUndo::get_undo_sensitive(doc)) {
7970         return;
7971     }
7973     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7975     if ( !repr->attribute("inkscape:connector-spacing") &&
7976             ( adj->value == defaultConnSpacing )) {
7977         // Don't need to update the repr if the attribute doesn't
7978         // exist and it is being set to the default value -- as will
7979         // happen at startup.
7980         return;
7981     }
7983     // quit if run by the attr_changed listener
7984     if (g_object_get_data( tbl, "freeze" )) {
7985         return;
7986     }
7988     // in turn, prevent listener from responding
7989     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7991     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7992     SP_OBJECT(desktop->namedview)->updateRepr();
7994     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7995     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7996         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7997         Geom::Matrix m = Geom::identity();
7998         avoid_item_move(&m, item);
7999     }
8001     if (items) {
8002         g_slist_free(items);
8003     }
8005     SPDocumentUndo::done(doc, SP_VERB_CONTEXT_CONNECTOR,
8006             _("Change connector spacing"));
8008     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
8011 static void sp_connector_graph_layout(void)
8013     if (!SP_ACTIVE_DESKTOP) {
8014         return;
8015     }
8016     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8018     // hack for clones, see comment in align-and-distribute.cpp
8019     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
8020     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
8022     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
8024     prefs->setInt("/options/clonecompensation/value", saved_compensation);
8026     SPDocumentUndo::done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
8029 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
8031     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8032     prefs->setBool("/tools/connector/directedlayout",
8033                 gtk_toggle_action_get_active( act ));
8036 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
8038     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8039     prefs->setBool("/tools/connector/avoidoverlaplayout",
8040                 gtk_toggle_action_get_active( act ));
8044 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
8046     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8047     prefs->setDouble("/tools/connector/length", adj->value);
8050 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
8051                                             gchar const *name, gchar const * /*old_value*/, gchar const * /*new_value*/,
8052                                             bool /*is_interactive*/, gpointer data)
8054     GtkWidget *tbl = GTK_WIDGET(data);
8056     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
8057         return;
8058     }
8059     if (strcmp(name, "inkscape:connector-spacing") == 0)
8060     {
8061         GtkAdjustment *adj = (GtkAdjustment*)
8062                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
8063         gdouble spacing = defaultConnSpacing;
8064         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
8066         gtk_adjustment_set_value(adj, spacing);
8067         gtk_adjustment_value_changed(adj);
8068     }
8070     spinbutton_defocus(GTK_OBJECT(tbl));
8073 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
8075     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
8076     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
8078     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
8079         cc_create_connection_point(cc);
8080     }
8083 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
8085     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
8086     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
8088     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
8089         cc_remove_connection_point(cc);
8090     }
8093 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
8094     NULL, /* child_added */
8095     NULL, /* child_removed */
8096     connector_tb_event_attr_changed,
8097     NULL, /* content_changed */
8098     NULL  /* order_changed */
8099 };
8101 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
8103     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
8104     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
8105     SPItem *item = selection->singleItem();
8106     if (SP_IS_PATH(item))
8107     {
8108         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
8109         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
8110         gtk_toggle_action_set_active(act, is_orthog);
8111         gtk_adjustment_set_value(adj, curvature);
8112     }
8116 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
8118     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8119     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
8121     // Editing mode toggle button
8122     {
8123         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
8124                                                       _("EditMode"),
8125                                                       _("Switch between connection point editing and connector drawing mode"),
8126                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
8127                                                       Inkscape::ICON_SIZE_DECORATION );
8128         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8130         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
8131         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
8132         g_object_set_data( holder, "mode", act );
8133         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
8134     }
8137     {
8138         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
8139                                           _("Avoid"),
8140                                           _("Make connectors avoid selected objects"),
8141                                           INKSCAPE_ICON_CONNECTOR_AVOID,
8142                                           secondarySize );
8143         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
8144         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8145     }
8147     {
8148         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
8149                                           _("Ignore"),
8150                                           _("Make connectors ignore selected objects"),
8151                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
8152                                           secondarySize );
8153         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
8154         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8155     }
8157     // Orthogonal connectors toggle button
8158     {
8159         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
8160                                                       _("Orthogonal"),
8161                                                       _("Make connector orthogonal or polyline"),
8162                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
8163                                                       Inkscape::ICON_SIZE_DECORATION );
8164         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8166         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
8167         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
8168         g_object_set_data( holder, "orthogonal", act );
8169         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
8170     }
8172     EgeAdjustmentAction* eact = 0;
8173     // Curvature spinbox
8174     eact = create_adjustment_action( "ConnectorCurvatureAction",
8175                                     _("Connector Curvature"), _("Curvature:"),
8176                                     _("The amount of connectors curvature"),
8177                                     "/tools/connector/curvature", defaultConnCurvature,
8178                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
8179                                     0, 100, 1.0, 10.0,
8180                                     0, 0, 0,
8181                                     connector_curvature_changed, 1, 0 );
8182     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8184     // Spacing spinbox
8185     eact = create_adjustment_action( "ConnectorSpacingAction",
8186                                     _("Connector Spacing"), _("Spacing:"),
8187                                     _("The amount of space left around objects by auto-routing connectors"),
8188                                     "/tools/connector/spacing", defaultConnSpacing,
8189                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
8190                                     0, 100, 1.0, 10.0,
8191                                     0, 0, 0,
8192                                     connector_spacing_changed, 1, 0 );
8193     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8195     // Graph (connector network) layout
8196     {
8197         InkAction* inky = ink_action_new( "ConnectorGraphAction",
8198                                           _("Graph"),
8199                                           _("Nicely arrange selected connector network"),
8200                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
8201                                           secondarySize );
8202         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
8203         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8204     }
8206     // Default connector length spinbox
8207     eact = create_adjustment_action( "ConnectorLengthAction",
8208                                      _("Connector Length"), _("Length:"),
8209                                      _("Ideal length for connectors when layout is applied"),
8210                                      "/tools/connector/length", 100,
8211                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
8212                                      10, 1000, 10.0, 100.0,
8213                                      0, 0, 0,
8214                                      connector_length_changed, 1, 0 );
8215     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8218     // Directed edges toggle button
8219     {
8220         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
8221                                                       _("Downwards"),
8222                                                       _("Make connectors with end-markers (arrows) point downwards"),
8223                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
8224                                                       Inkscape::ICON_SIZE_DECORATION );
8225         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8227         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
8228         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
8230         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
8231         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder));
8232     }
8234     // Avoid overlaps toggle button
8235     {
8236         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
8237                                                       _("Remove overlaps"),
8238                                                       _("Do not allow overlapping shapes"),
8239                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
8240                                                       Inkscape::ICON_SIZE_DECORATION );
8241         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
8243         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
8244         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
8246         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
8247     }
8250     // New connection point button
8251     {
8252         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
8253                                           _("New connection point"),
8254                                           _("Add a new connection point to the currently selected item"),
8255                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
8256                                           secondarySize );
8257         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
8258         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8259     }
8261     // Remove selected connection point button
8263     {
8264         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
8265                                           _("Remove connection point"),
8266                                           _("Remove the currently selected connection point"),
8267                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
8268                                           secondarySize );
8269         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
8270         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
8271     }
8274     // Code to watch for changes to the connector-spacing attribute in
8275     // the XML.
8276     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
8277     g_assert(repr != NULL);
8279     purge_repr_listener( holder, holder );
8281     if (repr) {
8282         g_object_set_data( holder, "repr", repr );
8283         Inkscape::GC::anchor(repr);
8284         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
8285         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
8286     }
8287 } // end of sp_connector_toolbox_prep()
8290 //#########################
8291 //##     Paintbucket     ##
8292 //#########################
8294 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
8296     gint channels = ege_select_one_action_get_active( act );
8297     flood_channels_set_channels( channels );
8300 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject * /*tbl*/)
8302     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8303     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
8306 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject * /*tbl*/)
8308     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8309     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
8312 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
8314     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
8315     SPUnit const *unit = tracker->getActiveUnit();
8316     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8318     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
8319     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
8322 static void paintbucket_defaults(GtkWidget *, GObject *tbl)
8324     // FIXME: make defaults settable via Inkscape Options
8325     struct KeyValue {
8326         char const *key;
8327         double value;
8328     } const key_values[] = {
8329         {"threshold", 15},
8330         {"offset", 0.0}
8331     };
8333     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
8334         KeyValue const &kv = key_values[i];
8335         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
8336         if ( adj ) {
8337             gtk_adjustment_set_value(adj, kv.value);
8338         }
8339     }
8341     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
8342     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
8343     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
8344     ege_select_one_action_set_active( autogap_action, 0 );
8347 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
8349     EgeAdjustmentAction* eact = 0;
8350     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
8352     {
8353         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
8355         GList* items = 0;
8356         gint count = 0;
8357         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
8358         {
8359             GtkTreeIter iter;
8360             gtk_list_store_append( model, &iter );
8361             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
8362             count++;
8363         }
8364         g_list_free( items );
8365         items = 0;
8366         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
8367         g_object_set( act1, "short_label", _("Fill by:"), NULL );
8368         ege_select_one_action_set_appearance( act1, "compact" );
8369         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
8370         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
8371         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
8372         g_object_set_data( holder, "channels_action", act1 );
8373     }
8375     // Spacing spinbox
8376     {
8377         eact = create_adjustment_action(
8378             "ThresholdAction",
8379             _("Fill Threshold"), _("Threshold:"),
8380             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
8381             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
8382             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
8383             0, 0, 0,
8384             paintbucket_threshold_changed, 1, 0 );
8386         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
8387         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8388     }
8390     // Create the units menu.
8391     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
8392     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
8393     if (!stored_unit.empty()) {
8394         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
8395     }
8396     g_object_set_data( holder, "tracker", tracker );
8397     {
8398         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
8399         gtk_action_group_add_action( mainActions, act );
8400     }
8402     // Offset spinbox
8403     {
8404         eact = create_adjustment_action(
8405             "OffsetAction",
8406             _("Grow/shrink by"), _("Grow/shrink by:"),
8407             _("The amount to grow (positive) or shrink (negative) the created fill path"),
8408             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
8409             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
8410             0, 0, 0,
8411             paintbucket_offset_changed, 1, 2);
8412         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
8414         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
8415     }
8417     /* Auto Gap */
8418     {
8419         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
8421         GList* items = 0;
8422         gint count = 0;
8423         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
8424         {
8425             GtkTreeIter iter;
8426             gtk_list_store_append( model, &iter );
8427             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
8428             count++;
8429         }
8430         g_list_free( items );
8431         items = 0;
8432         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
8433         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
8434         ege_select_one_action_set_appearance( act2, "compact" );
8435         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
8436         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
8437         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
8438         g_object_set_data( holder, "autogap_action", act2 );
8439     }
8441     /* Reset */
8442     {
8443         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
8444                                           _("Defaults"),
8445                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
8446                                           GTK_STOCK_CLEAR );
8447         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
8448         gtk_action_group_add_action( mainActions, act );
8449         gtk_action_set_sensitive( act, TRUE );
8450     }
8454 /*
8455   Local Variables:
8456   mode:c++
8457   c-file-style:"stroustrup"
8458   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
8459   indent-tabs-mode:nil
8460   fill-column:99
8461   End:
8462 */
8463 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :