Code

Fixed state sync for node tool booleans. Addressing bug #426032.
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@scislac.com>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *
16  * Copyright (C) 2004 David Turner
17  * Copyright (C) 2003 MenTaLguY
18  * Copyright (C) 1999-2008 authors
19  * Copyright (C) 2001-2002 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../pen-context.h"
69 #include "../preferences.h"
70 #include "../selection-chemistry.h"
71 #include "../selection.h"
72 #include "select-toolbar.h"
73 #include "../shape-editor.h"
74 #include "../shortcuts.h"
75 #include "../sp-clippath.h"
76 #include "../sp-ellipse.h"
77 #include "../sp-flowtext.h"
78 #include "../sp-mask.h"
79 #include "../sp-namedview.h"
80 #include "../sp-rect.h"
81 #include "../sp-spiral.h"
82 #include "../sp-star.h"
83 #include "../sp-text.h"
84 #include "../style.h"
85 #include "../svg/css-ostringstream.h"
86 #include "../tools-switch.h"
87 #include "../tweak-context.h"
88 #include "../spray-context.h"
89 #include "../ui/dialog/calligraphic-profile-rename.h"
90 #include "../ui/icon-names.h"
91 #include "../ui/tool/control-point-selection.h"
92 #include "../ui/tool/node-tool.h"
93 #include "../ui/tool/multi-path-manipulator.h"
94 #include "../ui/widget/style-swatch.h"
95 #include "../verbs.h"
96 #include "../widgets/button.h"
97 #include "../widgets/spinbutton-events.h"
98 #include "../widgets/spw-utilities.h"
99 #include "../widgets/widget-sizes.h"
100 #include "../xml/attribute-record.h"
101 #include "../xml/node-event-vector.h"
102 #include "../xml/repr.h"
103 #include "ui/uxmanager.h"
105 #include "toolbox.h"
107 #define ENABLE_TASK_SUPPORT 1
109 using Inkscape::UnitTracker;
110 using Inkscape::UI::UXManager;
112 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
113 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
115 enum BarId {
116     BAR_TOOL = 0,
117     BAR_AUX,
118     BAR_COMMANDS,
119     BAR_SNAP,
120 };
122 #define BAR_ID_KEY "BarIdValue"
123 #define HANDLE_POS_MARK "x-inkscape-pos"
125 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
133 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
137 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
139 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
140 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
141 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
142 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
144 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
147 #if ENABLE_TASK_SUPPORT
148 static void fireTaskChange( EgeSelectOneAction *act, SPDesktop *dt )
150     gint selected = ege_select_one_action_get_active( act );
151     UXManager::getInstance()->setTask(dt, selected);
153 #endif // ENABLE_TASK_SUPPORT
155 using Inkscape::UI::ToolboxFactory;
158 Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
159     static Inkscape::IconSize sizeChoices[] = {
160         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
161         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
162         Inkscape::ICON_SIZE_MENU
163     };
164     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
165     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
166     return sizeChoices[index];
169 static struct {
170     gchar const *type_name;
171     gchar const *data_name;
172     sp_verb_t verb;
173     sp_verb_t doubleclick_verb;
174 } const tools[] = {
175     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
176     { "InkNodeTool",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
177     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
178     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
179     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
180     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
181     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
182     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
183     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
184     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
185     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
186     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
187     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
188     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
189     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
190     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
191     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
192     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
193     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
194     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
195     { NULL, NULL, 0, 0 }
196 };
198 static struct {
199     gchar const *type_name;
200     gchar const *data_name;
201     GtkWidget *(*create_func)(SPDesktop *desktop);
202     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
203     gchar const *ui_name;
204     gint swatch_verb_id;
205     gchar const *swatch_tool;
206     gchar const *swatch_tip;
207 } const aux_toolboxes[] = {
208     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
209       SP_VERB_INVALID, 0, 0},
210     { "InkNodeTool",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
211       SP_VERB_INVALID, 0, 0},
212     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
213       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
214     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
215       SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
216     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
217       SP_VERB_INVALID, 0, 0},
218     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
219       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
220     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
221       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
222     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
223       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
224     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
225       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
226     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
227       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
228     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
229       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
230     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
231       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
232     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
233       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
234     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
235       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
236     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
237       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
238     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
239       SP_VERB_INVALID, 0, 0},
240     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
241       SP_VERB_INVALID, 0, 0},
242     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
243       SP_VERB_INVALID, 0, 0},
244     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
245       SP_VERB_INVALID, 0, 0},
246     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
247       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
248     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
249 };
251 #define TOOLBAR_SLIDER_HINT "full"
253 static gchar const * ui_descr =
254         "<ui>"
255         "  <toolbar name='SelectToolbar'>"
256         "    <toolitem action='EditSelectAll' />"
257         "    <toolitem action='EditSelectAllInAllLayers' />"
258         "    <toolitem action='EditDeselect' />"
259         "    <separator />"
260         "    <toolitem action='ObjectRotate90CCW' />"
261         "    <toolitem action='ObjectRotate90' />"
262         "    <toolitem action='ObjectFlipHorizontally' />"
263         "    <toolitem action='ObjectFlipVertically' />"
264         "    <separator />"
265         "    <toolitem action='SelectionToBack' />"
266         "    <toolitem action='SelectionLower' />"
267         "    <toolitem action='SelectionRaise' />"
268         "    <toolitem action='SelectionToFront' />"
269         "    <separator />"
270         "    <toolitem action='XAction' />"
271         "    <toolitem action='YAction' />"
272         "    <toolitem action='WidthAction' />"
273         "    <toolitem action='LockAction' />"
274         "    <toolitem action='HeightAction' />"
275         "    <toolitem action='UnitsAction' />"
276         "    <separator />"
277         "    <toolitem action='transform_affect_label' />"
278         "    <toolitem action='transform_stroke' />"
279         "    <toolitem action='transform_corners' />"
280         "    <toolitem action='transform_gradient' />"
281         "    <toolitem action='transform_pattern' />"
282         "  </toolbar>"
284         "  <toolbar name='NodeToolbar'>"
285         "    <separator />"
286         "    <toolitem action='NodeInsertAction' />"
287         "    <toolitem action='NodeDeleteAction' />"
288         "    <separator />"
289         "    <toolitem action='NodeJoinAction' />"
290         "    <toolitem action='NodeBreakAction' />"
291         "    <separator />"
292         "    <toolitem action='NodeJoinSegmentAction' />"
293         "    <toolitem action='NodeDeleteSegmentAction' />"
294         "    <separator />"
295         "    <toolitem action='NodeCuspAction' />"
296         "    <toolitem action='NodeSmoothAction' />"
297         "    <toolitem action='NodeSymmetricAction' />"
298         "    <toolitem action='NodeAutoAction' />"
299         "    <separator />"
300         "    <toolitem action='NodeLineAction' />"
301         "    <toolitem action='NodeCurveAction' />"
302         "    <separator />"
303         "    <toolitem action='ObjectToPath' />"
304         "    <toolitem action='StrokeToPath' />"
305         "    <separator />"
306         "    <toolitem action='NodeXAction' />"
307         "    <toolitem action='NodeYAction' />"
308         "    <toolitem action='NodeUnitsAction' />"
309         "    <separator />"
310         "    <toolitem action='ObjectEditClipPathAction' />"
311         "    <toolitem action='ObjectEditMaskPathAction' />"
312         "    <toolitem action='EditNextLPEParameterAction' />"
313         "    <separator />"
314         "    <toolitem action='NodesShowTransformHandlesAction' />"
315         "    <toolitem action='NodesShowHandlesAction' />"
316         "    <toolitem action='NodesShowHelperpath' />"
317         "  </toolbar>"
319         "  <toolbar name='TweakToolbar'>"
320         "    <toolitem action='TweakWidthAction' />"
321         "    <separator />"
322         "    <toolitem action='TweakForceAction' />"
323         "    <toolitem action='TweakPressureAction' />"
324         "    <separator />"
325         "    <toolitem action='TweakModeAction' />"
326         "    <separator />"
327         "    <toolitem action='TweakFidelityAction' />"
328         "    <separator />"
329         "    <toolitem action='TweakChannelsLabel' />"
330         "    <toolitem action='TweakDoH' />"
331         "    <toolitem action='TweakDoS' />"
332         "    <toolitem action='TweakDoL' />"
333         "    <toolitem action='TweakDoO' />"
334         "  </toolbar>"
336         "  <toolbar name='SprayToolbar'>"
337         "    <toolitem action='SprayModeAction' />"
338         "    <separator />"
339         "    <separator />"
340         "    <toolitem action='SprayWidthAction' />"
341         "    <toolitem action='SprayPressureAction' />"
342         "    <toolitem action='SprayPopulationAction' />"
343         "    <separator />"
344         "    <toolitem action='SprayRotationAction' />"
345         "    <toolitem action='SprayScaleAction' />"
346         "    <separator />"
347         "    <toolitem action='SprayStandard_deviationAction' />"
348         "    <toolitem action='SprayMeanAction' />"
349         "  </toolbar>"
351         "  <toolbar name='ZoomToolbar'>"
352         "    <toolitem action='ZoomIn' />"
353         "    <toolitem action='ZoomOut' />"
354         "    <separator />"
355         "    <toolitem action='Zoom1:0' />"
356         "    <toolitem action='Zoom1:2' />"
357         "    <toolitem action='Zoom2:1' />"
358         "    <separator />"
359         "    <toolitem action='ZoomSelection' />"
360         "    <toolitem action='ZoomDrawing' />"
361         "    <toolitem action='ZoomPage' />"
362         "    <toolitem action='ZoomPageWidth' />"
363         "    <separator />"
364         "    <toolitem action='ZoomPrev' />"
365         "    <toolitem action='ZoomNext' />"
366         "  </toolbar>"
368         "  <toolbar name='StarToolbar'>"
369         "    <separator />"
370         "    <toolitem action='StarStateAction' />"
371         "    <separator />"
372         "    <toolitem action='FlatAction' />"
373         "    <separator />"
374         "    <toolitem action='MagnitudeAction' />"
375         "    <toolitem action='SpokeAction' />"
376         "    <toolitem action='RoundednessAction' />"
377         "    <toolitem action='RandomizationAction' />"
378         "    <separator />"
379         "    <toolitem action='StarResetAction' />"
380         "  </toolbar>"
382         "  <toolbar name='RectToolbar'>"
383         "    <toolitem action='RectStateAction' />"
384         "    <toolitem action='RectWidthAction' />"
385         "    <toolitem action='RectHeightAction' />"
386         "    <toolitem action='RadiusXAction' />"
387         "    <toolitem action='RadiusYAction' />"
388         "    <toolitem action='RectUnitsAction' />"
389         "    <separator />"
390         "    <toolitem action='RectResetAction' />"
391         "  </toolbar>"
393         "  <toolbar name='3DBoxToolbar'>"
394         "    <toolitem action='3DBoxAngleXAction' />"
395         "    <toolitem action='3DBoxVPXStateAction' />"
396         "    <separator />"
397         "    <toolitem action='3DBoxAngleYAction' />"
398         "    <toolitem action='3DBoxVPYStateAction' />"
399         "    <separator />"
400         "    <toolitem action='3DBoxAngleZAction' />"
401         "    <toolitem action='3DBoxVPZStateAction' />"
402         "  </toolbar>"
404         "  <toolbar name='SpiralToolbar'>"
405         "    <toolitem action='SpiralStateAction' />"
406         "    <toolitem action='SpiralRevolutionAction' />"
407         "    <toolitem action='SpiralExpansionAction' />"
408         "    <toolitem action='SpiralT0Action' />"
409         "    <separator />"
410         "    <toolitem action='SpiralResetAction' />"
411         "  </toolbar>"
413         "  <toolbar name='PenToolbar'>"
414         "    <toolitem action='FreehandModeActionPen' />"
415         "    <separator />"
416         "    <toolitem action='SetPenShapeAction'/>"
417         "  </toolbar>"
419         "  <toolbar name='PencilToolbar'>"
420         "    <toolitem action='FreehandModeActionPencil' />"
421         "    <separator />"
422         "    <toolitem action='PencilToleranceAction' />"
423         "    <separator />"
424         "    <toolitem action='PencilResetAction' />"
425         "    <separator />"
426         "    <toolitem action='SetPencilShapeAction'/>"
427         "  </toolbar>"
429         "  <toolbar name='CalligraphyToolbar'>"
430         "    <separator />"
431         "    <toolitem action='SetProfileAction'/>"
432         "    <separator />"
433         "    <toolitem action='CalligraphyWidthAction' />"
434         "    <toolitem action='PressureAction' />"
435         "    <toolitem action='TraceAction' />"
436         "    <toolitem action='ThinningAction' />"
437         "    <separator />"
438         "    <toolitem action='AngleAction' />"
439         "    <toolitem action='TiltAction' />"
440         "    <toolitem action='FixationAction' />"
441         "    <separator />"
442         "    <toolitem action='CapRoundingAction' />"
443         "    <separator />"
444         "    <toolitem action='TremorAction' />"
445         "    <toolitem action='WiggleAction' />"
446         "    <toolitem action='MassAction' />"
447         "    <separator />"
448         "  </toolbar>"
450         "  <toolbar name='ArcToolbar'>"
451         "    <toolitem action='ArcStateAction' />"
452         "    <separator />"
453         "    <toolitem action='ArcStartAction' />"
454         "    <toolitem action='ArcEndAction' />"
455         "    <separator />"
456         "    <toolitem action='ArcOpenAction' />"
457         "    <separator />"
458         "    <toolitem action='ArcResetAction' />"
459         "    <separator />"
460         "  </toolbar>"
462         "  <toolbar name='PaintbucketToolbar'>"
463         "    <toolitem action='ChannelsAction' />"
464         "    <separator />"
465         "    <toolitem action='ThresholdAction' />"
466         "    <separator />"
467         "    <toolitem action='OffsetAction' />"
468         "    <toolitem action='PaintbucketUnitsAction' />"
469         "    <separator />"
470         "    <toolitem action='AutoGapAction' />"
471         "    <separator />"
472         "    <toolitem action='PaintbucketResetAction' />"
473         "  </toolbar>"
475         "  <toolbar name='EraserToolbar'>"
476         "    <toolitem action='EraserWidthAction' />"
477         "    <separator />"
478         "    <toolitem action='EraserModeAction' />"
479         "  </toolbar>"
481         "  <toolbar name='LPEToolToolbar'>"
482         "    <toolitem action='LPEToolModeAction' />"
483         "    <separator />"
484         "    <toolitem action='LPEShowBBoxAction' />"
485         "    <toolitem action='LPEBBoxFromSelectionAction' />"
486         "    <separator />"
487         "    <toolitem action='LPELineSegmentAction' />"
488         "    <separator />"
489         "    <toolitem action='LPEMeasuringAction' />"
490         "    <toolitem action='LPEToolUnitsAction' />"
491         "    <separator />"
492         "    <toolitem action='LPEOpenLPEDialogAction' />"
493         "  </toolbar>"
495         "  <toolbar name='DropperToolbar'>"
496         "    <toolitem action='DropperOpacityAction' />"
497         "    <toolitem action='DropperPickAlphaAction' />"
498         "    <toolitem action='DropperSetAlphaAction' />"
499         "  </toolbar>"
501         "  <toolbar name='ConnectorToolbar'>"
502 //        "    <toolitem action='ConnectorEditModeAction' />"
503         "    <toolitem action='ConnectorAvoidAction' />"
504         "    <toolitem action='ConnectorIgnoreAction' />"
505         "    <toolitem action='ConnectorOrthogonalAction' />"
506         "    <toolitem action='ConnectorCurvatureAction' />"
507         "    <toolitem action='ConnectorSpacingAction' />"
508         "    <toolitem action='ConnectorGraphAction' />"
509         "    <toolitem action='ConnectorLengthAction' />"
510         "    <toolitem action='ConnectorDirectedAction' />"
511         "    <toolitem action='ConnectorOverlapAction' />"
512 //        "    <toolitem action='ConnectorNewConnPointAction' />"
513 //        "    <toolitem action='ConnectorRemoveConnPointAction' />"
514         "  </toolbar>"
516         "</ui>"
519 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
521 static void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
523 static void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
524 static void update_tool_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
526 static void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
527 static void update_aux_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
529 static void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop);
530 static void update_commands_toolbox(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
532 static GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
533                                                                      Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
534                                                                      Inkscape::UI::View::View *view, GtkTooltips *tt);
536 class VerbAction : public Gtk::Action {
537 public:
538     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
540     virtual ~VerbAction();
541     virtual void set_active(bool active = true);
543 protected:
544     virtual Gtk::Widget* create_menu_item_vfunc();
545     virtual Gtk::Widget* create_tool_item_vfunc();
547     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
548     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
550     virtual void on_activate();
552 private:
553     Inkscape::Verb* verb;
554     Inkscape::Verb* verb2;
555     Inkscape::UI::View::View *view;
556     GtkTooltips *tooltips;
557     bool active;
559     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
560 };
563 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
565     Glib::RefPtr<VerbAction> result;
566     SPAction *action = verb->get_action(view);
567     if ( action ) {
568         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
569         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
570     }
572     return result;
575 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
576     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
577     verb(verb),
578     verb2(verb2),
579     view(view),
580     tooltips(tooltips),
581     active(false)
585 VerbAction::~VerbAction()
589 Gtk::Widget* VerbAction::create_menu_item_vfunc()
591 // First call in to get the icon rendered if present in SVG
592     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
593     delete widget;
594     widget = 0;
596     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
597 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
598     return widg;
601 Gtk::Widget* VerbAction::create_tool_item_vfunc()
603 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
604     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small");
605     GtkWidget* toolbox = 0;
606     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
607                                                                           SP_BUTTON_TYPE_TOGGLE,
608                                                                           verb,
609                                                                           verb2,
610                                                                           view,
611                                                                           tooltips );
612     if ( active ) {
613         sp_button_toggle_set_down( SP_BUTTON(button), active);
614     }
615     gtk_widget_show_all( button );
616     Gtk::Widget* wrapped = Glib::wrap(button);
617     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
618     holder->add(*wrapped);
620 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
621     return holder;
624 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
626 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
627     Gtk::Action::connect_proxy_vfunc(proxy);
630 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
632 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
633     Gtk::Action::disconnect_proxy_vfunc(proxy);
636 void VerbAction::set_active(bool active)
638     this->active = active;
639     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
640     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
641         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
642         if (ti) {
643             // *should* have one child that is the SPButton
644             Gtk::Widget* child = ti->get_child();
645             if ( child && SP_IS_BUTTON(child->gobj()) ) {
646                 SPButton* button = SP_BUTTON(child->gobj());
647                 sp_button_toggle_set_down( button, active );
648             }
649         }
650     }
653 void VerbAction::on_activate()
655     if ( verb ) {
656         SPAction *action = verb->get_action(view);
657         if ( action ) {
658             sp_action_perform(action, 0);
659         }
660     }
663 /* Global text entry widgets necessary for update */
664 /* GtkWidget *dropper_rgb_entry,
665           *dropper_opacity_entry ; */
666 // should be made a private member once this is converted to class
668 static void delete_connection(GObject * /*obj*/, sigc::connection *connection)
670     connection->disconnect();
671     delete connection;
674 static void purge_repr_listener( GObject* /*obj*/, GObject* tbl )
676     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
677     if (oldrepr) { // remove old listener
678         sp_repr_remove_listener_by_data(oldrepr, tbl);
679         Inkscape::GC::release(oldrepr);
680         oldrepr = 0;
681         g_object_set_data( tbl, "repr", NULL );
682     }
685 // ------------------------------------------------------
687 /**
688  * A simple mediator class that keeps UI controls matched to the preference values they set.
689  */
690 class PrefPusher : public Inkscape::Preferences::Observer
692 public:
693     /**
694      * Constructor for a boolean value that syncs to the supplied path.
695      * Initializes the widget to the current preference stored state and registers callbacks
696      * for widget changes and preference changes.
697      *
698      * @param act the widget to synchronize preference with.
699      * @param path the path to the preference the widget is synchronized with.
700      * @param callback function to invoke when changes are pushed.
701      * @param cbData data to be passed on to the callback function.
702      */
703     PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*) = 0, GObject *cbData = 0 );
705     /**
706      * Destructor that unregisters the preference callback.
707      */
708     virtual ~PrefPusher();
710     /**
711      * Callback method invoked when the preference setting changes.
712      */
713     virtual void notify(Inkscape::Preferences::Entry const &new_val);
715 private:
716     /**
717      * Callback hook invoked when the widget changes.
718      *
719      * @param act the toggle action widget that was changed.
720      * @param self the PrefPusher instance the callback was registered to.
721      */
722     static void toggleCB( GtkToggleAction *act, PrefPusher *self );
724     /**
725      * Method to handle the widget change.
726      */
727     void handleToggled();
729     GtkToggleAction *act;
730     void (*callback)(GObject*);
731     GObject *cbData;
732     bool freeze;
733 };
735 PrefPusher::PrefPusher( GtkToggleAction *act, Glib::ustring const &path, void (*callback)(GObject*), GObject *cbData ) :
736     Observer(path),
737     act(act),
738     callback(callback),
739     cbData(cbData),
740     freeze(false)
742     g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggleCB), this);
743     freeze = true;
744     gtk_toggle_action_set_active( act, Inkscape::Preferences::get()->getBool(observed_path, true) );
745     freeze = false;
747     Inkscape::Preferences::get()->addObserver(*this);
750 PrefPusher::~PrefPusher()
752     Inkscape::Preferences::get()->removeObserver(*this);
755 void PrefPusher::toggleCB( GtkToggleAction * /*act*/, PrefPusher *self )
757     if (self) {
758         self->handleToggled();
759     }
762 void PrefPusher::handleToggled()
764     if (!freeze) {
765         freeze = true;
766         Inkscape::Preferences::get()->setBool(observed_path, gtk_toggle_action_get_active( act ));
767         if (callback) {
768             (*callback)(cbData);
769         }
770         freeze = false;
771     }
774 void PrefPusher::notify(Inkscape::Preferences::Entry const &newVal)
776     bool newBool = newVal.getBool();
777     bool oldBool = gtk_toggle_action_get_active(act);
779     if (!freeze && (newBool != oldBool)) {
780         gtk_toggle_action_set_active( act, newBool );
781     }
784 static void delete_prefspusher(GtkObject * /*obj*/, PrefPusher *watcher )
786     delete watcher;
789 // ------------------------------------------------------
792 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
793                                                              Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
794                                                              Inkscape::UI::View::View *view, GtkTooltips *tt)
796     SPAction *action = verb->get_action(view);
797     if (!action) {
798         return NULL;
799     }
801     SPAction *doubleclick_action;
802     if (doubleclick_verb) {
803         doubleclick_action = doubleclick_verb->get_action(view);
804     } else {
805         doubleclick_action = NULL;
806     }
808     /* fixme: Handle sensitive/unsensitive */
809     /* fixme: Implement sp_button_new_from_action */
810     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
811     gtk_widget_show(b);
814     unsigned int shortcut = sp_shortcut_get_primary(verb);
815     if (shortcut) {
816         gchar key[256];
817         sp_ui_shortcut_string(shortcut, key);
818         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
819         if ( t ) {
820             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
821         }
822         g_free(tip);
823     } else {
824         if ( t ) {
825             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
826         }
827     }
829     return b;
833 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
835     SPAction* targetAction = SP_ACTION(user_data);
836     if ( targetAction ) {
837         sp_action_perform( targetAction, NULL );
838     }
841 static void sp_action_action_set_sensitive(SPAction * /*action*/, unsigned int sensitive, void *data)
843     if ( data ) {
844         GtkAction* act = GTK_ACTION(data);
845         gtk_action_set_sensitive( act, sensitive );
846     }
849 static SPActionEventVector action_event_vector = {
850     {NULL},
851     NULL,
852     NULL,
853     sp_action_action_set_sensitive,
854     NULL,
855     NULL
856 };
858 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
860     GtkAction* act = 0;
862     SPAction* targetAction = verb->get_action(view);
863     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
864     act = GTK_ACTION(inky);
865     gtk_action_set_sensitive( act, targetAction->sensitive );
867     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
869     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
870     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
872     return act;
875 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
877     Inkscape::UI::View::View *view = desktop;
878     gint verbsToUse[] = {
879         // disabled until we have icons for them:
880         //find
881         //SP_VERB_EDIT_TILE,
882         //SP_VERB_EDIT_UNTILE,
883         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
884         SP_VERB_DIALOG_DISPLAY,
885         SP_VERB_DIALOG_FILL_STROKE,
886         SP_VERB_DIALOG_NAMEDVIEW,
887         SP_VERB_DIALOG_TEXT,
888         SP_VERB_DIALOG_XML_EDITOR,
889         SP_VERB_DIALOG_LAYERS,
890         SP_VERB_EDIT_CLONE,
891         SP_VERB_EDIT_COPY,
892         SP_VERB_EDIT_CUT,
893         SP_VERB_EDIT_DUPLICATE,
894         SP_VERB_EDIT_PASTE,
895         SP_VERB_EDIT_REDO,
896         SP_VERB_EDIT_UNDO,
897         SP_VERB_EDIT_UNLINK_CLONE,
898         SP_VERB_FILE_EXPORT,
899         SP_VERB_FILE_IMPORT,
900         SP_VERB_FILE_NEW,
901         SP_VERB_FILE_OPEN,
902         SP_VERB_FILE_PRINT,
903         SP_VERB_FILE_SAVE,
904         SP_VERB_OBJECT_TO_CURVE,
905         SP_VERB_SELECTION_GROUP,
906         SP_VERB_SELECTION_OUTLINE,
907         SP_VERB_SELECTION_UNGROUP,
908         SP_VERB_ZOOM_1_1,
909         SP_VERB_ZOOM_1_2,
910         SP_VERB_ZOOM_2_1,
911         SP_VERB_ZOOM_DRAWING,
912         SP_VERB_ZOOM_IN,
913         SP_VERB_ZOOM_NEXT,
914         SP_VERB_ZOOM_OUT,
915         SP_VERB_ZOOM_PAGE,
916         SP_VERB_ZOOM_PAGE_WIDTH,
917         SP_VERB_ZOOM_PREV,
918         SP_VERB_ZOOM_SELECTION,
919     };
921     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
923     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
924     Glib::RefPtr<Gtk::ActionGroup> mainActions;
925     if ( groups.find(desktop) != groups.end() ) {
926         mainActions = groups[desktop];
927     }
929     if ( !mainActions ) {
930         mainActions = Gtk::ActionGroup::create("main");
931         groups[desktop] = mainActions;
932     }
934     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
935         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
936         if ( verb ) {
937             if (!mainActions->get_action(verb->get_id())) {
938                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
939                 mainActions->add(Glib::wrap(act));
940             }
941         }
942     }
944     if ( !mainActions->get_action("ToolZoom") ) {
945         GtkTooltips *tt = gtk_tooltips_new();
946         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
947             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
948             if ( va ) {
949                 mainActions->add(va);
950                 if ( i == 0 ) {
951                     va->set_active(true);
952                 }
953             }
954         }
955     }
957 #if ENABLE_TASK_SUPPORT
958     if ( !mainActions->get_action("TaskSetAction") ) {
959         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
961         GtkTreeIter iter;
962         gtk_list_store_append( model, &iter );
963         gtk_list_store_set( model, &iter,
964                             0, _("Default"),
965                             1, _("Default interface setup"),
966                             -1 );
968         gtk_list_store_append( model, &iter );
969         gtk_list_store_set( model, &iter,
970                             0, _("Custom"),
971                             1, _("Set the custom task"),
972                             -1 );
974         gtk_list_store_append( model, &iter );
975         gtk_list_store_set( model, &iter,
976                             0, _("Wide"),
977                             1, _("Setup for widescreen work"),
978                             -1 );
980         EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) );
981         g_object_set( act, "short_label", _("Task:"), NULL );
982         mainActions->add(Glib::wrap(GTK_ACTION(act)));
983         //g_object_set_data( holder, "mode_action", act );
985         ege_select_one_action_set_appearance( act, "minimal" );
986         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
987         //ege_select_one_action_set_icon_size( act, secondarySize );
988         ege_select_one_action_set_tooltip_column( act, 1  );
990         //ege_select_one_action_set_active( act, mode );
991         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop );
992     }
993 #endif // ENABLE_TASK_SUPPORT
995     return mainActions;
999 static void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
1001     gtk_widget_set_size_request( widget,
1002                                  widget->allocation.width,
1003                                  widget->allocation.height );
1006 static void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
1008     gtk_widget_set_size_request( widget, -1, -1 );
1011 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
1013     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
1015     gtk_widget_set_sensitive(tb, FALSE);
1017     GtkWidget *hb = 0;
1018     if ( UXManager::getInstance()->isFloatWindowProblem() ) {
1019         hb = gtk_event_box_new(); // A simple, neutral container.
1020     } else {
1021         hb = gtk_handle_box_new();
1022         gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
1023         gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
1024         gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
1025     }
1027     gtk_container_add(GTK_CONTAINER(hb), tb);
1028     gtk_widget_show(GTK_WIDGET(tb));
1030     sigc::connection* conn = new sigc::connection;
1031     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
1033     if ( GTK_IS_HANDLE_BOX(hb) ) {
1034         g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
1035         g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
1036     }
1038     gpointer val = GINT_TO_POINTER(id);
1039     g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
1041     return hb;
1044 GtkWidget *ToolboxFactory::createToolToolbox()
1046     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1048     return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
1051 GtkWidget *ToolboxFactory::createAuxToolbox()
1053     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1055     return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
1058 //####################################
1059 //# Commands Bar
1060 //####################################
1062 GtkWidget *ToolboxFactory::createCommandsToolbox()
1064     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1066     return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
1069 GtkWidget *ToolboxFactory::createSnapToolbox()
1071     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
1073     return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
1076 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
1077                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
1078                                                        Glib::ustring const &path, gdouble def,
1079                                                        GtkWidget *focusTarget,
1080                                                        GtkWidget *us,
1081                                                        GObject *dataKludge,
1082                                                        gboolean altx, gchar const *altx_mark,
1083                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
1084                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
1085                                                        void (*callback)(GtkAdjustment *, GObject *),
1086                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
1088     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1089     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
1090                                                              lower, upper, step, page, 0 ) );
1091     if (us) {
1092         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
1093     }
1095     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
1097     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
1098     if ( shortLabel ) {
1099         g_object_set( act, "short_label", shortLabel, NULL );
1100     }
1102     if ( (descrCount > 0) && descrLabels && descrValues ) {
1103         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
1104     }
1106     if ( focusTarget ) {
1107         ege_adjustment_action_set_focuswidget( act, focusTarget );
1108     }
1110     if ( altx && altx_mark ) {
1111         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1112     }
1114     if ( dataKludge ) {
1115         // Rather lame, but it's the only place where we need to get the entry name
1116         // but we don't have an Entry
1117         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
1118     }
1120     // Using a cast just to make sure we pass in the right kind of function pointer
1121     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1123     return act;
1127 //####################################
1128 //# node editing callbacks
1129 //####################################
1131 /** Temporary hack: Returns the node tool in the active desktop.
1132  * Will go away during tool refactoring. */
1133 static InkNodeTool *get_node_tool()
1135     InkNodeTool *tool = 0;
1136     if (SP_ACTIVE_DESKTOP ) {
1137         SPEventContext *ec = SP_ACTIVE_DESKTOP->event_context;
1138         if (INK_IS_NODE_TOOL(ec)) {
1139             tool = static_cast<InkNodeTool*>(ec);
1140         }
1141     }
1142     return tool;
1145 static void sp_node_path_edit_add(void)
1147     InkNodeTool *nt = get_node_tool();
1148     if (nt) {
1149         nt->_multipath->insertNodes();
1150     }
1153 static void sp_node_path_edit_delete(void)
1155     InkNodeTool *nt = get_node_tool();
1156     if (nt) {
1157         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1158         nt->_multipath->deleteNodes(prefs->getBool("/tools/nodes/delete_preserves_shape", true));
1159     }
1162 static void sp_node_path_edit_delete_segment(void)
1164     InkNodeTool *nt = get_node_tool();
1165     if (nt) {
1166         nt->_multipath->deleteSegments();
1167     }
1170 static void sp_node_path_edit_break(void)
1172     InkNodeTool *nt = get_node_tool();
1173     if (nt) {
1174         nt->_multipath->breakNodes();
1175     }
1178 static void sp_node_path_edit_join(void)
1180     InkNodeTool *nt = get_node_tool();
1181     if (nt) {
1182         nt->_multipath->joinNodes();
1183     }
1186 static void sp_node_path_edit_join_segment(void)
1188     InkNodeTool *nt = get_node_tool();
1189     if (nt) {
1190         nt->_multipath->joinSegments();
1191     }
1194 static void sp_node_path_edit_toline(void)
1196     InkNodeTool *nt = get_node_tool();
1197     if (nt) {
1198         nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT);
1199     }
1202 static void sp_node_path_edit_tocurve(void)
1204     InkNodeTool *nt = get_node_tool();
1205     if (nt) {
1206         nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER);
1207     }
1210 static void sp_node_path_edit_cusp(void)
1212     InkNodeTool *nt = get_node_tool();
1213     if (nt) {
1214         nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP);
1215     }
1218 static void sp_node_path_edit_smooth(void)
1220     InkNodeTool *nt = get_node_tool();
1221     if (nt) {
1222         nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH);
1223     }
1226 static void sp_node_path_edit_symmetrical(void)
1228     InkNodeTool *nt = get_node_tool();
1229     if (nt) {
1230         nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC);
1231     }
1234 static void sp_node_path_edit_auto(void)
1236     InkNodeTool *nt = get_node_tool();
1237     if (nt) {
1238         nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO);
1239     }
1242 static void sp_node_path_edit_nextLPEparam(GtkAction * /*act*/, gpointer data) {
1243     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1246 /* is called when the node selection is modified */
1247 static void sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1249     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1250     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1251     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1252     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1254     // quit if run by the attr_changed listener
1255     if (g_object_get_data( tbl, "freeze" )) {
1256         return;
1257     }
1259     // in turn, prevent listener from responding
1260     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1262     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1263     SPUnit const *unit = tracker->getActiveUnit();
1265     InkNodeTool *nt = get_node_tool();
1266     if (!nt || nt->_selected_nodes->empty()) {
1267         // no path selected
1268         gtk_action_set_sensitive(xact, FALSE);
1269         gtk_action_set_sensitive(yact, FALSE);
1270     } else {
1271         gtk_action_set_sensitive(xact, TRUE);
1272         gtk_action_set_sensitive(yact, TRUE);
1273         Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1274         Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1275         Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint();
1277         if (oldx != mid[Geom::X]) {
1278             gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit));
1279         }
1280         if (oldy != mid[Geom::Y]) {
1281             gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit));
1282         }
1283     }
1285     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1288 static void sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d)
1290     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1291     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1293     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1294     SPUnit const *unit = tracker->getActiveUnit();
1296     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1297         prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"),
1298             sp_units_get_pixels(adj->value, *unit));
1299     }
1301     // quit if run by the attr_changed listener
1302     if (g_object_get_data( tbl, "freeze" )) {
1303         return;
1304     }
1306     // in turn, prevent listener from responding
1307     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1309     InkNodeTool *nt = get_node_tool();
1310     if (nt && !nt->_selected_nodes->empty()) {
1311         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1312         double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d];
1313         Geom::Point delta(0,0);
1314         delta[d] = val - oldval;
1315         nt->_multipath->move(delta);
1316     }
1318     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1321 static void sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1323     sp_node_path_value_changed(adj, tbl, Geom::X);
1326 static void sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1328     sp_node_path_value_changed(adj, tbl, Geom::Y);
1331 static void sp_node_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
1333     {
1334     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1335     SPItem *item = selection->singleItem();
1336     if (item && SP_IS_LPE_ITEM(item)) {
1337        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1338            gtk_action_set_sensitive(w, TRUE);
1339        } else {
1340            gtk_action_set_sensitive(w, FALSE);
1341        }
1342     } else {
1343        gtk_action_set_sensitive(w, FALSE);
1344     }
1345     }
1348 static void sp_node_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1350     sp_node_toolbox_sel_changed (selection, tbl);
1355 //################################
1356 //##    Node Editing Toolbox    ##
1357 //################################
1359 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1361     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1362     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1363     g_object_set_data( holder, "tracker", tracker );
1365     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1367     {
1368         InkAction* inky = ink_action_new( "NodeInsertAction",
1369                                           _("Insert node"),
1370                                           _("Insert new nodes into selected segments"),
1371                                           INKSCAPE_ICON_NODE_ADD,
1372                                           secondarySize );
1373         g_object_set( inky, "short_label", _("Insert"), NULL );
1374         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1375         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1376     }
1378     {
1379         InkAction* inky = ink_action_new( "NodeDeleteAction",
1380                                           _("Delete node"),
1381                                           _("Delete selected nodes"),
1382                                           INKSCAPE_ICON_NODE_DELETE,
1383                                           secondarySize );
1384         g_object_set( inky, "short_label", _("Delete"), NULL );
1385         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1386         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1387     }
1389     {
1390         InkAction* inky = ink_action_new( "NodeJoinAction",
1391                                           _("Join nodes"),
1392                                           _("Join selected nodes"),
1393                                           INKSCAPE_ICON_NODE_JOIN,
1394                                           secondarySize );
1395         g_object_set( inky, "short_label", _("Join"), NULL );
1396         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1397         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398     }
1400     {
1401         InkAction* inky = ink_action_new( "NodeBreakAction",
1402                                           _("Break nodes"),
1403                                           _("Break path at selected nodes"),
1404                                           INKSCAPE_ICON_NODE_BREAK,
1405                                           secondarySize );
1406         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1407         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408     }
1411     {
1412         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1413                                           _("Join with segment"),
1414                                           _("Join selected endnodes with a new segment"),
1415                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1416                                           secondarySize );
1417         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1418         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1419     }
1421     {
1422         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1423                                           _("Delete segment"),
1424                                           _("Delete segment between two non-endpoint nodes"),
1425                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1426                                           secondarySize );
1427         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429     }
1431     {
1432         InkAction* inky = ink_action_new( "NodeCuspAction",
1433                                           _("Node Cusp"),
1434                                           _("Make selected nodes corner"),
1435                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1436                                           secondarySize );
1437         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1438         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1439     }
1441     {
1442         InkAction* inky = ink_action_new( "NodeSmoothAction",
1443                                           _("Node Smooth"),
1444                                           _("Make selected nodes smooth"),
1445                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1446                                           secondarySize );
1447         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1448         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1449     }
1451     {
1452         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1453                                           _("Node Symmetric"),
1454                                           _("Make selected nodes symmetric"),
1455                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1456                                           secondarySize );
1457         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1458         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1459     }
1461     {
1462         InkAction* inky = ink_action_new( "NodeAutoAction",
1463                                           _("Node Auto"),
1464                                           _("Make selected nodes auto-smooth"),
1465                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1466                                           secondarySize );
1467         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1468         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1469     }
1471     {
1472         InkAction* inky = ink_action_new( "NodeLineAction",
1473                                           _("Node Line"),
1474                                           _("Make selected segments lines"),
1475                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1476                                           secondarySize );
1477         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1478         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1479     }
1481     {
1482         InkAction* inky = ink_action_new( "NodeCurveAction",
1483                                           _("Node Curve"),
1484                                           _("Make selected segments curves"),
1485                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1486                                           secondarySize );
1487         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1488         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1489     }
1491     {
1492         InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction",
1493                                                       _("Show Transform Handles"),
1494                                                       _("Show node transformation handles"),
1495                                                       "node-transform",
1496                                                       secondarySize );
1497         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1498         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_transform_handles");
1499         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1500     }
1502     {
1503         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1504                                                       _("Show Handles"),
1505                                                       _("Show the Bezier handles of selected nodes"),
1506                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1507                                                       secondarySize );
1508         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1509         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_handles");
1510         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1511     }
1513     {
1514         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1515                                                       _("Show Outline"),
1516                                                       _("Show the outline of the path"),
1517                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1518                                                       secondarySize );
1519         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1520         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/nodes/show_outline");
1521         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1522     }
1524     {
1525         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1526                                           _("Next path effect parameter"),
1527                                           _("Show next path effect parameter for editing"),
1528                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1529                                           secondarySize );
1530         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1531         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1532         g_object_set_data( holder, "nodes_lpeedit", inky);
1533     }
1535     {
1536         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction",
1537                                           _("Edit clipping paths"),
1538                                           _("Show editing controls for clipping paths of selected objects"),
1539                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1540                                           secondarySize );
1541         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1542         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_clipping_paths");
1543         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1544     }
1546     {
1547         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction",
1548                                           _("Edit masks"),
1549                                           _("Show editing controls for masks of selected objects"),
1550                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1551                                           secondarySize );
1552         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1553         PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(inky), "/tools/nodes/edit_masks");
1554         g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
1555     }
1557     /* X coord of selected node(s) */
1558     {
1559         EgeAdjustmentAction* eact = 0;
1560         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1561         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1562         eact = create_adjustment_action( "NodeXAction",
1563                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1564                                          "/tools/nodes/Xcoord", 0,
1565                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1566                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1567                                          labels, values, G_N_ELEMENTS(labels),
1568                                          sp_node_path_x_value_changed );
1569         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1570         g_object_set_data( holder, "nodes_x_action", eact );
1571         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1572         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1573     }
1575     /* Y coord of selected node(s) */
1576     {
1577         EgeAdjustmentAction* eact = 0;
1578         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1579         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1580         eact = create_adjustment_action( "NodeYAction",
1581                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1582                                          "/tools/nodes/Ycoord", 0,
1583                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1584                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1585                                          labels, values, G_N_ELEMENTS(labels),
1586                                          sp_node_path_y_value_changed );
1587         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1588         g_object_set_data( holder, "nodes_y_action", eact );
1589         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1590         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1591     }
1593     // add the units menu
1594     {
1595         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1596         gtk_action_group_add_action( mainActions, act );
1597     }
1600     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1602     //watch selection
1603     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1605     sigc::connection *c_selection_changed =
1606         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1607                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1608     pool->add_connection ("selection-changed", c_selection_changed);
1610     sigc::connection *c_selection_modified =
1611         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1612                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1613     pool->add_connection ("selection-modified", c_selection_modified);
1615     sigc::connection *c_subselection_changed =
1616         new sigc::connection (desktop->connectToolSubselectionChanged
1617                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1618     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1620     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1622     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1623 } // end of sp_node_toolbox_prep()
1626 //########################
1627 //##    Zoom Toolbox    ##
1628 //########################
1630 static void sp_zoom_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1632     // no custom GtkAction setup needed
1633 } // end of sp_zoom_toolbox_prep()
1635 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1637     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1638                                                                               "event_context_connection"));
1640     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1642     SetupFunction setup_func = 0;
1643     UpdateFunction update_func = 0;
1645     switch (id) {
1646         case BAR_TOOL:
1647             setup_func = setup_tool_toolbox;
1648             update_func = update_tool_toolbox;
1649             break;
1651         case BAR_AUX:
1652             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1653             setup_func = setup_aux_toolbox;
1654             update_func = update_aux_toolbox;
1655             break;
1657         case BAR_COMMANDS:
1658             setup_func = setup_commands_toolbox;
1659             update_func = update_commands_toolbox;
1660             break;
1662         case BAR_SNAP:
1663             setup_func = setup_snap_toolbox;
1664             update_func = updateSnapToolbox;
1665             break;
1666         default:
1667             g_warning("Unexpected toolbox id encountered.");
1668     }
1670     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1671     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1673     if (old_desktop) {
1674         GList *children, *iter;
1676         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1677         for ( iter = children ; iter ; iter = iter->next ) {
1678             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1679         }
1680         g_list_free(children);
1681     }
1683     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1685     if (desktop && setup_func && update_func) {
1686         gtk_widget_set_sensitive(toolbox, TRUE);
1687         setup_func(toolbox, desktop);
1688         update_func(desktop, desktop->event_context, toolbox);
1689         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1690     } else {
1691         gtk_widget_set_sensitive(toolbox, FALSE);
1692     }
1694 } // end of sp_toolbox_set_desktop()
1697 static void setupToolboxCommon( GtkWidget *toolbox,
1698                                 SPDesktop *desktop,
1699                                 gchar const *descr,
1700                                 gchar const* toolbarName,
1701                                 gchar const* sizePref )
1703     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1704     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1706     GtkUIManager* mgr = gtk_ui_manager_new();
1707     GError* errVal = 0;
1709     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1711     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1712     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1714     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1715     if ( prefs->getBool("/toolbox/icononly", true) ) {
1716         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1717     }
1719     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1720     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1722     if (GTK_IS_HANDLE_BOX(toolbox)) {
1723         // g_message("GRABBING ORIENTATION   [%s]", toolbarName);
1724         GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1725         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1726     } else {
1727         GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK )));
1728         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1729     }
1730     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1731     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1733     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1735     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1736     if ( child ) {
1737         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1738     }
1740     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1743 #define noDUMP_DETAILS 1
1745 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1747 #if DUMP_DETAILS
1748     g_message("Set orientation for %p to be %d", toolbox, orientation);
1749     GType type = GTK_WIDGET_TYPE(toolbox);
1750     g_message("        [%s]", g_type_name(type));
1751     g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1752 #endif
1754     GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1755     GtkHandleBox* handleBox = 0;
1757     if (GTK_IS_BIN(toolbox)) {
1758 #if DUMP_DETAILS
1759         g_message("            is a BIN");
1760 #endif // DUMP_DETAILS
1761         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1762         if (child) {
1763 #if DUMP_DETAILS
1764             GType type2 = GTK_WIDGET_TYPE(child);
1765             g_message("            child    [%s]", g_type_name(type2));
1766 #endif // DUMP_DETAILS
1768             if (GTK_IS_BOX(child)) {
1769 #if DUMP_DETAILS
1770                 g_message("                is a BOX");
1771 #endif // DUMP_DETAILS
1773                 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1774                 if (children) {
1775                     for (GList* curr = children; curr; curr = g_list_next(curr)) {
1776                         GtkWidget* child2 = GTK_WIDGET(curr->data);
1777 #if DUMP_DETAILS
1778                         GType type3 = GTK_WIDGET_TYPE(child2);
1779                         g_message("                child2   [%s]", g_type_name(type3));
1780 #endif // DUMP_DETAILS
1782                         if (GTK_IS_CONTAINER(child2)) {
1783                             GList* children2 = gtk_container_get_children(GTK_CONTAINER(child2));
1784                             if (children2) {
1785                                 for (GList* curr2 = children2; curr2; curr2 = g_list_next(curr2)) {
1786                                     GtkWidget* child3 = GTK_WIDGET(curr2->data);
1787 #if DUMP_DETAILS
1788                                     GType type4 = GTK_WIDGET_TYPE(child3);
1789                                     g_message("                    child3   [%s]", g_type_name(type4));
1790 #endif // DUMP_DETAILS
1791                                     if (GTK_IS_TOOLBAR(child3)) {
1792                                         GtkToolbar* childBar = GTK_TOOLBAR(child3);
1793                                         gtk_toolbar_set_orientation(childBar, orientation);
1794                                     }
1795                                 }
1796                                 g_list_free(children2);
1797                             }
1798                         }
1801                         if (GTK_IS_TOOLBAR(child2)) {
1802                             GtkToolbar* childBar = GTK_TOOLBAR(child2);
1803                             gtk_toolbar_set_orientation(childBar, orientation);
1804                             if (GTK_IS_HANDLE_BOX(toolbox)) {
1805                                 handleBox = GTK_HANDLE_BOX(toolbox);
1806                             }
1807                         } else {
1808                             g_message("need to add dynamic switch");
1809                         }
1810                     }
1811                     g_list_free(children);
1812                 } else {
1813                     // The call is being made before the toolbox proper has been setup.
1814                     if (GTK_IS_HANDLE_BOX(toolbox)) {
1815                         handleBox = GTK_HANDLE_BOX(toolbox);
1816                     } else {
1817                         g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos));
1818                     }
1819                 }
1820             } else if (GTK_IS_TOOLBAR(child)) {
1821                 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1822                 gtk_toolbar_set_orientation( toolbar, orientation );
1823                 if (GTK_IS_HANDLE_BOX(toolbox)) {
1824                     handleBox = GTK_HANDLE_BOX(toolbox);
1825                 }
1826             }
1827         }
1828     }
1830     if (handleBox) {
1831         gtk_handle_box_set_handle_position(handleBox, pos);
1832     }
1835 void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1837     gchar const * descr =
1838         "<ui>"
1839         "  <toolbar name='ToolToolbar'>"
1840         "    <toolitem action='ToolSelector' />"
1841         "    <toolitem action='ToolNode' />"
1842         "    <toolitem action='ToolTweak' />"
1843         "    <toolitem action='ToolSpray' />"
1844         "    <toolitem action='ToolZoom' />"
1845         "    <toolitem action='ToolRect' />"
1846         "    <toolitem action='Tool3DBox' />"
1847         "    <toolitem action='ToolArc' />"
1848         "    <toolitem action='ToolStar' />"
1849         "    <toolitem action='ToolSpiral' />"
1850         "    <toolitem action='ToolPencil' />"
1851         "    <toolitem action='ToolPen' />"
1852         "    <toolitem action='ToolCalligraphic' />"
1853         "    <toolitem action='ToolEraser' />"
1854 //        "    <toolitem action='ToolLPETool' />"
1855         "    <toolitem action='ToolPaintBucket' />"
1856         "    <toolitem action='ToolText' />"
1857         "    <toolitem action='ToolConnector' />"
1858         "    <toolitem action='ToolGradient' />"
1859         "    <toolitem action='ToolDropper' />"
1860         "  </toolbar>"
1861         "</ui>";
1863     setupToolboxCommon( toolbox, desktop, descr,
1864                         "/ui/ToolToolbar",
1865                         "/toolbox/tools/small");
1868 void update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget * /*toolbox*/ )
1870     gchar const *const tname = ( eventcontext
1871                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1872                                  : NULL );
1873     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1875     for (int i = 0 ; tools[i].type_name ; i++ ) {
1876         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1877         if ( act ) {
1878             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1879             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1880             if ( verbAct ) {
1881                 verbAct->set_active(setActive);
1882             }
1883         }
1884     }
1887 void setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1889     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1890     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1891     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1892     GtkUIManager* mgr = gtk_ui_manager_new();
1893     GError* errVal = 0;
1894     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1895     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1897     std::map<std::string, GtkWidget*> dataHolders;
1899     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1900         if ( aux_toolboxes[i].prep_func ) {
1901             // converted to GtkActions and UIManager
1903             GtkWidget* kludge = gtk_toolbar_new();
1904             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1905             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1906             dataHolders[aux_toolboxes[i].type_name] = kludge;
1907             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1908         } else {
1910             GtkWidget *sub_toolbox = 0;
1911             if (aux_toolboxes[i].create_func == NULL) {
1912                 sub_toolbox = sp_empty_toolbox_new(desktop);
1913             } else {
1914                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1915             }
1917             gtk_size_group_add_widget( grouper, sub_toolbox );
1919             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1920             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1922         }
1923     }
1925     // Second pass to create toolbars *after* all GtkActions are created
1926     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1927         if ( aux_toolboxes[i].prep_func ) {
1928             // converted to GtkActions and UIManager
1930             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1932             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1933             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1935             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1936             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1937             g_free( tmp );
1938             tmp = 0;
1940             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1941                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1942             }
1944             Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1945             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1947             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1949             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1950                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1951                 swatch->setDesktop( desktop );
1952                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1953                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1954                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1955                 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 );
1956             }
1958             gtk_widget_show_all( holder );
1959             sp_set_font_size_smaller( holder );
1961             gtk_size_group_add_widget( grouper, holder );
1963             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1964             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1965         }
1966     }
1968     g_object_unref( G_OBJECT(grouper) );
1971 void update_aux_toolbox(SPDesktop * /*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1973     gchar const *tname = ( eventcontext
1974                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1975                            : NULL );
1976     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1977         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1978         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1979             gtk_widget_show_all(sub_toolbox);
1980             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1981         } else {
1982             gtk_widget_hide(sub_toolbox);
1983         }
1984     }
1987 void setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1989     gchar const * descr =
1990         "<ui>"
1991         "  <toolbar name='CommandsToolbar'>"
1992         "    <toolitem action='FileNew' />"
1993         "    <toolitem action='FileOpen' />"
1994         "    <toolitem action='FileSave' />"
1995         "    <toolitem action='FilePrint' />"
1996         "    <separator />"
1997         "    <toolitem action='FileImport' />"
1998         "    <toolitem action='FileExport' />"
1999         "    <separator />"
2000         "    <toolitem action='EditUndo' />"
2001         "    <toolitem action='EditRedo' />"
2002         "    <separator />"
2003         "    <toolitem action='EditCopy' />"
2004         "    <toolitem action='EditCut' />"
2005         "    <toolitem action='EditPaste' />"
2006         "    <separator />"
2007         "    <toolitem action='ZoomSelection' />"
2008         "    <toolitem action='ZoomDrawing' />"
2009         "    <toolitem action='ZoomPage' />"
2010         "    <separator />"
2011         "    <toolitem action='EditDuplicate' />"
2012         "    <toolitem action='EditClone' />"
2013         "    <toolitem action='EditUnlinkClone' />"
2014         "    <separator />"
2015         "    <toolitem action='SelectionGroup' />"
2016         "    <toolitem action='SelectionUnGroup' />"
2017         "    <separator />"
2018         "    <toolitem action='DialogFillStroke' />"
2019         "    <toolitem action='DialogText' />"
2020         "    <toolitem action='DialogLayers' />"
2021         "    <toolitem action='DialogXMLEditor' />"
2022         "    <toolitem action='DialogAlignDistribute' />"
2023         "    <separator />"
2024         "    <toolitem action='DialogPreferences' />"
2025         "    <toolitem action='DialogDocumentProperties' />"
2026 #if ENABLE_TASK_SUPPORT
2027         "    <separator />"
2028         "    <toolitem action='TaskSetAction' />"
2029 #endif // ENABLE_TASK_SUPPORT
2030         "  </toolbar>"
2031         "</ui>";
2033     setupToolboxCommon( toolbox, desktop, descr,
2034                         "/ui/CommandsToolbar",
2035                         "/toolbox/small" );
2038 void update_commands_toolbox(SPDesktop * /*desktop*/, SPEventContext * /*eventcontext*/, GtkWidget * /*toolbox*/)
2042 static void toggle_snap_callback(GtkToggleAction *act, gpointer data) //data points to the toolbox
2044     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
2045         return;
2046     }
2048     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
2049     g_assert(ptr != NULL);
2051     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
2052     SPNamedView *nv = sp_desktop_namedview(dt);
2053     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
2055     if (dt == NULL || nv == NULL) {
2056         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
2057         return;
2058     }
2060     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
2062     if (repr == NULL) {
2063         g_warning("This namedview doesn't have a xml representation attached!");
2064         return;
2065     }
2067     bool saved = sp_document_get_undo_sensitive(doc);
2068     sp_document_set_undo_sensitive(doc, false);
2070     bool v = false;
2071     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
2073     switch (attr) {
2074         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
2075             dt->toggleSnapGlobal();
2076             break;
2077         case SP_ATTR_INKSCAPE_SNAP_BBOX:
2078             v = nv->snap_manager.snapprefs.getSnapModeBBox();
2079             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
2080             break;
2081         case SP_ATTR_INKSCAPE_BBOX_PATHS:
2082             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
2083             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
2084             break;
2085         case SP_ATTR_INKSCAPE_BBOX_NODES:
2086             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
2087             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
2088             break;
2089         case SP_ATTR_INKSCAPE_SNAP_NODES:
2090             v = nv->snap_manager.snapprefs.getSnapModeNode();
2091             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
2092             break;
2093         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
2094             v = nv->snap_manager.snapprefs.getSnapToItemPath();
2095             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
2096             break;
2097         case SP_ATTR_INKSCAPE_OBJECT_NODES:
2098             v = nv->snap_manager.snapprefs.getSnapToItemNode();
2099             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
2100             break;
2101         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
2102             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
2103             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2104             break;
2105         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2106             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2107             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2108             break;
2109         case SP_ATTR_INKSCAPE_SNAP_CENTER:
2110             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2111             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2112             break;
2113         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2114             v = nv->snap_manager.snapprefs.getSnapToGrids();
2115             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2116             break;
2117         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2118             v = nv->snap_manager.snapprefs.getSnapToGuides();
2119             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2120             break;
2121         case SP_ATTR_INKSCAPE_SNAP_PAGE:
2122             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2123             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2124             break;
2125             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2126               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2127               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2128               break;*/
2129         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2130             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2131             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2132             break;
2133         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2134             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2135             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2136             break;
2137         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2138             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2139             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2140             break;
2141         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2142             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2143             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2144             break;
2145         default:
2146             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2147             break;
2148     }
2150     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2151     doc->setModifiedSinceSave();
2153     sp_document_set_undo_sensitive(doc, saved);
2156 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2158     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2160     gchar const * descr =
2161         "<ui>"
2162         "  <toolbar name='SnapToolbar'>"
2163         "    <toolitem action='ToggleSnapGlobal' />"
2164         "    <separator />"
2165         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2166         "    <toolitem action='ToggleSnapToBBoxPath' />"
2167         "    <toolitem action='ToggleSnapToBBoxNode' />"
2168         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2169         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2170         "    <separator />"
2171         "    <toolitem action='ToggleSnapFromNode' />"
2172         "    <toolitem action='ToggleSnapToItemPath' />"
2173         "    <toolitem action='ToggleSnapToPathIntersections' />"
2174         "    <toolitem action='ToggleSnapToItemNode' />"
2175         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2176         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2177         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2178         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2179         "    <separator />"
2180         "    <toolitem action='ToggleSnapToPageBorder' />"
2181         "    <toolitem action='ToggleSnapToGrids' />"
2182         "    <toolitem action='ToggleSnapToGuides' />"
2183         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2184         "  </toolbar>"
2185         "</ui>";
2187     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2189     {
2190         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2191                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2192                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2194         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2195         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2196     }
2198     {
2199         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2200                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2201                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2203         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2204         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2205     }
2207     {
2208         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2209                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2210                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2212         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2213         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2214     }
2216     {
2217         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2218                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2219                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2221         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2222         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2223     }
2225     {
2226         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2227                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2228                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2229                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2231         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2232         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2233     }
2235     {
2236         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2237                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2238                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2240         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2241         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2242     }
2244     {
2245         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2246                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2248         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2249         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2250     }
2252     {
2253         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2254                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2255                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2257         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2258         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2259     }
2261     {
2262         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2263                                                      _("Path intersections"), _("Snap to path intersections"),
2264                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2266         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2267         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2268     }
2270     {
2271         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2272                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2273                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2275         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2276         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2277     }
2279     {
2280         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2281                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2282                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2284         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2285         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2286     }
2288     {
2289         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2290                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2291                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2293         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2294         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2295     }
2297     {
2298         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2299                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2300                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2302         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2303         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2304     }
2306     {
2307         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2308                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2309                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2311         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2312         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2313     }
2315     {
2316         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2317                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2318                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2320         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2321         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2322     }
2324     {
2325         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2326                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2327                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2329         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2330         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2331     }
2333     {
2334         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2335                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2336                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2338         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2339         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2340     }
2342     /*{
2343       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2344       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2345       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2346       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2348       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2349       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2350       }*/
2352     setupToolboxCommon( toolbox, desktop, descr,
2353                         "/ui/SnapToolbar",
2354                         "/toolbox/secondary" );
2357 Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox)
2359     Glib::ustring name;
2360     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
2361     switch(id) {
2362         case BAR_TOOL:
2363             name = "ToolToolbar";
2364             break;
2365         case BAR_AUX:
2366             name = "AuxToolbar";
2367             break;
2368         case BAR_COMMANDS:
2369             name = "CommandsToolbar";
2370             break;
2371         case BAR_SNAP:
2372             name = "SnapToolbar";
2373             break;
2374     }
2376     return name;
2379 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext * /*eventcontext*/, GtkWidget *toolbox)
2381     g_assert(desktop != NULL);
2382     g_assert(toolbox != NULL);
2384     SPNamedView *nv = sp_desktop_namedview(desktop);
2385     if (nv == NULL) {
2386         g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2387         return;
2388     }
2390     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2392     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2393     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2394     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2395     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2396     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2397     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2398     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2399     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2400     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2401     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2402     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2403     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2404     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2405     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2406     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2407     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2408     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2409     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2412     if (!act1) {
2413         return; // The snap actions haven't been defined yet (might be the case during startup)
2414     }
2416     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2417     // changes in our document because we're only updating the UI;
2418     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2419     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2421     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2422     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2424     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2425     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2426     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2428     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2429     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2430     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2431     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2432     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2433     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2434     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2435     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2437     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2438     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2439     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2441     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2442     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2443     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2444     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2445     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2446     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2447     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2448     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2449     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2450     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2451     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2452     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2453     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2454     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2455     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2457     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2458     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2459     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2460     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2462     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2463     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2464     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2465     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2468     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2471 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2473     gtk_widget_show(toolbox_toplevel);
2474     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2476     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2477     if (!shown_toolbox) {
2478         return;
2479     }
2480     gtk_widget_show(toolbox);
2482     gtk_widget_show_all(shown_toolbox);
2485 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2487     GtkWidget *tbl = gtk_toolbar_new();
2488     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2489     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2491     gtk_widget_show_all(tbl);
2492     sp_set_font_size_smaller (tbl);
2494     return tbl;
2497 #define MODE_LABEL_WIDTH 70
2499 //########################
2500 //##       Star         ##
2501 //########################
2503 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2505     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2507     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2508         // do not remember prefs if this call is initiated by an undo change, because undoing object
2509         // creation sets bogus values to its attributes before it is deleted
2510         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2511         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2512     }
2514     // quit if run by the attr_changed listener
2515     if (g_object_get_data( dataKludge, "freeze" )) {
2516         return;
2517     }
2519     // in turn, prevent listener from responding
2520     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2522     bool modmade = false;
2524     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2525     GSList const *items = selection->itemList();
2526     for (; items != NULL; items = items->next) {
2527         if (SP_IS_STAR((SPItem *) items->data)) {
2528             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2529             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2530             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2531                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2532                                     + M_PI / (gint)adj->value));
2533             SP_OBJECT((SPItem *) items->data)->updateRepr();
2534             modmade = true;
2535         }
2536     }
2537     if (modmade) {
2538         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2539                          _("Star: Change number of corners"));
2540     }
2542     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2545 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2547     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2549     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2550         if (!IS_NAN(adj->value)) {
2551             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2552             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2553         }
2554     }
2556     // quit if run by the attr_changed listener
2557     if (g_object_get_data( dataKludge, "freeze" )) {
2558         return;
2559     }
2561     // in turn, prevent listener from responding
2562     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2564     bool modmade = false;
2565     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2566     GSList const *items = selection->itemList();
2567     for (; items != NULL; items = items->next) {
2568         if (SP_IS_STAR((SPItem *) items->data)) {
2569             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2571             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2572             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2573             if (r2 < r1) {
2574                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2575             } else {
2576                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2577             }
2579             SP_OBJECT((SPItem *) items->data)->updateRepr();
2580             modmade = true;
2581         }
2582     }
2584     if (modmade) {
2585         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2586                          _("Star: Change spoke ratio"));
2587     }
2589     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2592 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2594     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2595     bool flat = ege_select_one_action_get_active( act ) == 0;
2597     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2598         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2599         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2600     }
2602     // quit if run by the attr_changed listener
2603     if (g_object_get_data( dataKludge, "freeze" )) {
2604         return;
2605     }
2607     // in turn, prevent listener from responding
2608     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2610     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2611     GSList const *items = selection->itemList();
2612     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2613     bool modmade = false;
2615     if ( prop_action ) {
2616         gtk_action_set_sensitive( prop_action, !flat );
2617     }
2619     for (; items != NULL; items = items->next) {
2620         if (SP_IS_STAR((SPItem *) items->data)) {
2621             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2622             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2623             SP_OBJECT((SPItem *) items->data)->updateRepr();
2624             modmade = true;
2625         }
2626     }
2628     if (modmade) {
2629         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2630                          flat ? _("Make polygon") : _("Make star"));
2631     }
2633     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2636 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2638     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2640     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2641         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2642         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2643     }
2645     // quit if run by the attr_changed listener
2646     if (g_object_get_data( dataKludge, "freeze" )) {
2647         return;
2648     }
2650     // in turn, prevent listener from responding
2651     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2653     bool modmade = false;
2655     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2656     GSList const *items = selection->itemList();
2657     for (; items != NULL; items = items->next) {
2658         if (SP_IS_STAR((SPItem *) items->data)) {
2659             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2660             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2661             SP_OBJECT(items->data)->updateRepr();
2662             modmade = true;
2663         }
2664     }
2665     if (modmade) {
2666         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2667                          _("Star: Change rounding"));
2668     }
2670     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2673 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2675     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2677     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2678         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2679         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2680     }
2682     // quit if run by the attr_changed listener
2683     if (g_object_get_data( dataKludge, "freeze" )) {
2684         return;
2685     }
2687     // in turn, prevent listener from responding
2688     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2690     bool modmade = false;
2692     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2693     GSList const *items = selection->itemList();
2694     for (; items != NULL; items = items->next) {
2695         if (SP_IS_STAR((SPItem *) items->data)) {
2696             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2697             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2698             SP_OBJECT(items->data)->updateRepr();
2699             modmade = true;
2700         }
2701     }
2702     if (modmade) {
2703         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2704                          _("Star: Change randomization"));
2705     }
2707     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2711 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2712                                        gchar const * /*old_value*/, gchar const * /*new_value*/,
2713                                        bool /*is_interactive*/, gpointer data)
2715     GtkWidget *tbl = GTK_WIDGET(data);
2717     // quit if run by the _changed callbacks
2718     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2719         return;
2720     }
2722     // in turn, prevent callbacks from responding
2723     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2725     GtkAdjustment *adj = 0;
2727     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2728     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2730     if (!strcmp(name, "inkscape:randomized")) {
2731         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2732         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2733     } else if (!strcmp(name, "inkscape:rounded")) {
2734         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2735         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2736     } else if (!strcmp(name, "inkscape:flatsided")) {
2737         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2738         char const *flatsides = repr->attribute("inkscape:flatsided");
2739         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2740         if ( flatsides && !strcmp(flatsides,"false") ) {
2741             ege_select_one_action_set_active( flat_action, 1 );
2742             gtk_action_set_sensitive( prop_action, TRUE );
2743         } else {
2744             ege_select_one_action_set_active( flat_action, 0 );
2745             gtk_action_set_sensitive( prop_action, FALSE );
2746         }
2747     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2748         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2749         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2750         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2751         if (r2 < r1) {
2752             gtk_adjustment_set_value(adj, r2/r1);
2753         } else {
2754             gtk_adjustment_set_value(adj, r1/r2);
2755         }
2756     } else if (!strcmp(name, "sodipodi:sides")) {
2757         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2758         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2759     }
2761     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2765 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2767     NULL, /* child_added */
2768     NULL, /* child_removed */
2769     star_tb_event_attr_changed,
2770     NULL, /* content_changed */
2771     NULL  /* order_changed */
2772 };
2775 /**
2776  *  \param selection Should not be NULL.
2777  */
2778 static void
2779 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2781     int n_selected = 0;
2782     Inkscape::XML::Node *repr = NULL;
2784     purge_repr_listener( tbl, tbl );
2786     for (GSList const *items = selection->itemList();
2787          items != NULL;
2788          items = items->next)
2789     {
2790         if (SP_IS_STAR((SPItem *) items->data)) {
2791             n_selected++;
2792             repr = SP_OBJECT_REPR((SPItem *) items->data);
2793         }
2794     }
2796     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2798     if (n_selected == 0) {
2799         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2800     } else if (n_selected == 1) {
2801         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2803         if (repr) {
2804             g_object_set_data( tbl, "repr", repr );
2805             Inkscape::GC::anchor(repr);
2806             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2807             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2808         }
2809     } else {
2810         // FIXME: implement averaging of all parameters for multiple selected stars
2811         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2812         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2813     }
2817 static void sp_stb_defaults( GtkWidget * /*widget*/, GObject *dataKludge )
2819     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2820     // callbacks to lump all the changes for all selected objects in one undo step
2822     GtkAdjustment *adj = 0;
2824     // fixme: make settable in prefs!
2825     gint mag = 5;
2826     gdouble prop = 0.5;
2827     gboolean flat = FALSE;
2828     gdouble randomized = 0;
2829     gdouble rounded = 0;
2831     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2832     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2834     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2835     gtk_action_set_sensitive( sb2, !flat );
2837     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2838     gtk_adjustment_set_value(adj, mag);
2839     gtk_adjustment_value_changed(adj);
2841     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2842     gtk_adjustment_set_value(adj, prop);
2843     gtk_adjustment_value_changed(adj);
2845     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2846     gtk_adjustment_set_value(adj, rounded);
2847     gtk_adjustment_value_changed(adj);
2849     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2850     gtk_adjustment_set_value(adj, randomized);
2851     gtk_adjustment_value_changed(adj);
2855 // public:
2856 void sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2858     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2859     if (wide) {
2860         gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2861     }
2862     GtkWidget *l = gtk_label_new(NULL);
2863     gtk_label_set_markup(GTK_LABEL(l), title);
2864     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2865     if ( GTK_IS_TOOLBAR(tbl) ) {
2866         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2867     } else {
2868         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2869     }
2870     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2874 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2876     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2878     {
2879         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2880         ege_output_action_set_use_markup( act, TRUE );
2881         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2882         g_object_set_data( holder, "mode_action", act );
2883     }
2885     {
2886         EgeAdjustmentAction* eact = 0;
2887         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2888         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2890         /* Flatsided checkbox */
2891         {
2892             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2894             GtkTreeIter iter;
2895             gtk_list_store_append( model, &iter );
2896             gtk_list_store_set( model, &iter,
2897                                 0, _("Polygon"),
2898                                 1, _("Regular polygon (with one handle) instead of a star"),
2899                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2900                                 -1 );
2902             gtk_list_store_append( model, &iter );
2903             gtk_list_store_set( model, &iter,
2904                                 0, _("Star"),
2905                                 1, _("Star instead of a regular polygon (with one handle)"),
2906                                 2, INKSCAPE_ICON_DRAW_STAR,
2907                                 -1 );
2909             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2910             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2911             g_object_set_data( holder, "flat_action", act );
2913             ege_select_one_action_set_appearance( act, "full" );
2914             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2915             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2916             ege_select_one_action_set_icon_column( act, 2 );
2917             ege_select_one_action_set_icon_size( act, secondarySize );
2918             ege_select_one_action_set_tooltip_column( act, 1  );
2920             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2921             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2922         }
2924         /* Magnitude */
2925         {
2926         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2927         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2928         eact = create_adjustment_action( "MagnitudeAction",
2929                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2930                                          "/tools/shapes/star/magnitude", 3,
2931                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2932                                          3, 1024, 1, 5,
2933                                          labels, values, G_N_ELEMENTS(labels),
2934                                          sp_stb_magnitude_value_changed,
2935                                          1.0, 0 );
2936         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2937         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2938         }
2940         /* Spoke ratio */
2941         {
2942         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2943         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2944         eact = create_adjustment_action( "SpokeAction",
2945                                          _("Spoke ratio"), _("Spoke ratio:"),
2946                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2947                                          // Base radius is the same for the closest handle.
2948                                          _("Base radius to tip radius ratio"),
2949                                          "/tools/shapes/star/proportion", 0.5,
2950                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2951                                          0.01, 1.0, 0.01, 0.1,
2952                                          labels, values, G_N_ELEMENTS(labels),
2953                                          sp_stb_proportion_value_changed );
2954         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2955         g_object_set_data( holder, "prop_action", eact );
2956         }
2958         if ( !isFlatSided ) {
2959             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2960         } else {
2961             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2962         }
2964         /* Roundedness */
2965         {
2966         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2967         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2968         eact = create_adjustment_action( "RoundednessAction",
2969                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2970                                          "/tools/shapes/star/rounded", 0.0,
2971                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2972                                          -10.0, 10.0, 0.01, 0.1,
2973                                          labels, values, G_N_ELEMENTS(labels),
2974                                          sp_stb_rounded_value_changed );
2975         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2976         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2977         }
2979         /* Randomization */
2980         {
2981         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2982         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2983         eact = create_adjustment_action( "RandomizationAction",
2984                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2985                                          "/tools/shapes/star/randomized", 0.0,
2986                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2987                                          -10.0, 10.0, 0.001, 0.01,
2988                                          labels, values, G_N_ELEMENTS(labels),
2989                                          sp_stb_randomized_value_changed, 0.1, 3 );
2990         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2991         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2992         }
2993     }
2995     {
2996         /* Reset */
2997         {
2998             GtkAction* act = gtk_action_new( "StarResetAction",
2999                                              _("Defaults"),
3000                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3001                                              GTK_STOCK_CLEAR );
3002             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
3003             gtk_action_group_add_action( mainActions, act );
3004             gtk_action_set_sensitive( act, TRUE );
3005         }
3006     }
3008     sigc::connection *connection = new sigc::connection(
3009         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
3010         );
3011     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3012     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3016 //########################
3017 //##       Rect         ##
3018 //########################
3020 static void sp_rtb_sensitivize( GObject *tbl )
3022     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
3023     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
3024     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
3026     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
3027         gtk_action_set_sensitive( not_rounded, FALSE );
3028     } else {
3029         gtk_action_set_sensitive( not_rounded, TRUE );
3030     }
3034 static void sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
3035                                  void (*setter)(SPRect *, gdouble))
3037     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3039     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
3040     SPUnit const *unit = tracker->getActiveUnit();
3042     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3043         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3044         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
3045     }
3047     // quit if run by the attr_changed listener
3048     if (g_object_get_data( tbl, "freeze" )) {
3049         return;
3050     }
3052     // in turn, prevent listener from responding
3053     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
3055     bool modmade = false;
3056     Inkscape::Selection *selection = sp_desktop_selection(desktop);
3057     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
3058         if (SP_IS_RECT(items->data)) {
3059             if (adj->value != 0) {
3060                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
3061             } else {
3062                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
3063             }
3064             modmade = true;
3065         }
3066     }
3068     sp_rtb_sensitivize( tbl );
3070     if (modmade) {
3071         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
3072                                    _("Change rectangle"));
3073     }
3075     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3078 static void sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
3080     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
3083 static void sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
3085     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
3088 static void sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
3090     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
3093 static void sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
3095     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
3100 static void sp_rtb_defaults( GtkWidget * /*widget*/, GObject *obj)
3102     GtkAdjustment *adj = 0;
3104     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
3105     gtk_adjustment_set_value(adj, 0.0);
3106     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
3107     gtk_adjustment_value_changed(adj);
3109     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
3110     gtk_adjustment_set_value(adj, 0.0);
3111     gtk_adjustment_value_changed(adj);
3113     sp_rtb_sensitivize( obj );
3116 static void rect_tb_event_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*name*/,
3117                                        gchar const * /*old_value*/, gchar const * /*new_value*/,
3118                                        bool /*is_interactive*/, gpointer data)
3120     GObject *tbl = G_OBJECT(data);
3122     // quit if run by the _changed callbacks
3123     if (g_object_get_data( tbl, "freeze" )) {
3124         return;
3125     }
3127     // in turn, prevent callbacks from responding
3128     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3130     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3131     SPUnit const *unit = tracker->getActiveUnit();
3133     gpointer item = g_object_get_data( tbl, "item" );
3134     if (item && SP_IS_RECT(item)) {
3135         {
3136             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3137             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3138             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3139         }
3141         {
3142             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3143             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3144             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3145         }
3147         {
3148             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3149             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3150             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3151         }
3153         {
3154             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3155             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3156             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3157         }
3158     }
3160     sp_rtb_sensitivize( tbl );
3162     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3166 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3167     NULL, /* child_added */
3168     NULL, /* child_removed */
3169     rect_tb_event_attr_changed,
3170     NULL, /* content_changed */
3171     NULL  /* order_changed */
3172 };
3174 /**
3175  *  \param selection should not be NULL.
3176  */
3177 static void sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3179     int n_selected = 0;
3180     Inkscape::XML::Node *repr = NULL;
3181     SPItem *item = NULL;
3183     if ( g_object_get_data( tbl, "repr" ) ) {
3184         g_object_set_data( tbl, "item", NULL );
3185     }
3186     purge_repr_listener( tbl, tbl );
3188     for (GSList const *items = selection->itemList();
3189          items != NULL;
3190          items = items->next) {
3191         if (SP_IS_RECT((SPItem *) items->data)) {
3192             n_selected++;
3193             item = (SPItem *) items->data;
3194             repr = SP_OBJECT_REPR(item);
3195         }
3196     }
3198     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3200     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3202     if (n_selected == 0) {
3203         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3205         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3206         gtk_action_set_sensitive(w, FALSE);
3207         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3208         gtk_action_set_sensitive(h, FALSE);
3210     } else if (n_selected == 1) {
3211         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3212         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3214         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3215         gtk_action_set_sensitive(w, TRUE);
3216         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3217         gtk_action_set_sensitive(h, TRUE);
3219         if (repr) {
3220             g_object_set_data( tbl, "repr", repr );
3221             g_object_set_data( tbl, "item", item );
3222             Inkscape::GC::anchor(repr);
3223             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3224             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3225         }
3226     } else {
3227         // FIXME: implement averaging of all parameters for multiple selected
3228         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3229         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3230         sp_rtb_sensitivize( tbl );
3231     }
3235 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3237     EgeAdjustmentAction* eact = 0;
3238     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3240     {
3241         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3242         ege_output_action_set_use_markup( act, TRUE );
3243         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3244         g_object_set_data( holder, "mode_action", act );
3245     }
3247     // rx/ry units menu: create
3248     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3249     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3250     // fixme: add % meaning per cent of the width/height
3251     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3252     g_object_set_data( holder, "tracker", tracker );
3254     /* W */
3255     {
3256         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3257         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3258         eact = create_adjustment_action( "RectWidthAction",
3259                                          _("Width"), _("W:"), _("Width of rectangle"),
3260                                          "/tools/shapes/rect/width", 0,
3261                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3262                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3263                                          labels, values, G_N_ELEMENTS(labels),
3264                                          sp_rtb_width_value_changed );
3265         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3266         g_object_set_data( holder, "width_action", eact );
3267         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3268         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3269     }
3271     /* H */
3272     {
3273         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3274         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3275         eact = create_adjustment_action( "RectHeightAction",
3276                                          _("Height"), _("H:"), _("Height of rectangle"),
3277                                          "/tools/shapes/rect/height", 0,
3278                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3279                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3280                                          labels, values, G_N_ELEMENTS(labels),
3281                                          sp_rtb_height_value_changed );
3282         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3283         g_object_set_data( holder, "height_action", eact );
3284         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3285         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3286     }
3288     /* rx */
3289     {
3290         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3291         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3292         eact = create_adjustment_action( "RadiusXAction",
3293                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3294                                          "/tools/shapes/rect/rx", 0,
3295                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3296                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3297                                          labels, values, G_N_ELEMENTS(labels),
3298                                          sp_rtb_rx_value_changed);
3299         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3300         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3301     }
3303     /* ry */
3304     {
3305         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3306         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3307         eact = create_adjustment_action( "RadiusYAction",
3308                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3309                                          "/tools/shapes/rect/ry", 0,
3310                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3311                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3312                                          labels, values, G_N_ELEMENTS(labels),
3313                                          sp_rtb_ry_value_changed);
3314         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3315         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3316     }
3318     // add the units menu
3319     {
3320         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3321         gtk_action_group_add_action( mainActions, act );
3322     }
3324     /* Reset */
3325     {
3326         InkAction* inky = ink_action_new( "RectResetAction",
3327                                           _("Not rounded"),
3328                                           _("Make corners sharp"),
3329                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3330                                           secondarySize );
3331         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3332         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3333         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3334         g_object_set_data( holder, "not_rounded", inky );
3335     }
3337     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3338     sp_rtb_sensitivize( holder );
3340     sigc::connection *connection = new sigc::connection(
3341         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3342         );
3343     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3344     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3347 //########################
3348 //##       3D Box       ##
3349 //########################
3351 // normalize angle so that it lies in the interval [0,360]
3352 static double box3d_normalize_angle (double a) {
3353     double angle = a + ((int) (a/360.0))*360;
3354     if (angle < 0) {
3355         angle += 360.0;
3356     }
3357     return angle;
3360 static void box3d_set_button_and_adjustment(Persp3D *persp,
3361                                             Proj::Axis axis,
3362                                             GtkAdjustment *adj,
3363                                             GtkAction *act,
3364                                             GtkToggleAction *tact)
3366     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3367     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3368     //       are reset).
3369     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3371     if (is_infinite) {
3372         gtk_toggle_action_set_active(tact, TRUE);
3373         gtk_action_set_sensitive(act, TRUE);
3375         double angle = persp3d_get_infinite_angle(persp, axis);
3376         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3377             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3378         }
3379     } else {
3380         gtk_toggle_action_set_active(tact, FALSE);
3381         gtk_action_set_sensitive(act, FALSE);
3382     }
3385 static void box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data)
3387     if (!persp_repr) {
3388         g_print ("No perspective given to box3d_resync_toolbar().\n");
3389         return;
3390     }
3392     GtkWidget *tbl = GTK_WIDGET(data);
3393     GtkAdjustment *adj = 0;
3394     GtkAction *act = 0;
3395     GtkToggleAction *tact = 0;
3396     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3397     if (!persp) {
3398         // Hmm, is it an error if this happens?
3399         return;
3400     }
3401     {
3402         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3403         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3404         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3406         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3407     }
3408     {
3409         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3410         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3411         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3413         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3414     }
3415     {
3416         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3417         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3418         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3420         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3421     }
3424 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr,
3425                                               gchar const * /*name*/,
3426                                               gchar const * /*old_value*/,
3427                                               gchar const * /*new_value*/,
3428                                               bool /*is_interactive*/,
3429                                               gpointer data)
3431     GtkWidget *tbl = GTK_WIDGET(data);
3433     // quit if run by the attr_changed or selection changed listener
3434     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3435         return;
3436     }
3438     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3439     // sp_document_maybe_done() when the document is undo insensitive)
3440     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3442     // TODO: Only update the appropriate part of the toolbar
3443 //    if (!strcmp(name, "inkscape:vp_z")) {
3444         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3445 //    }
3447     Persp3D *persp = persp3d_get_from_repr(repr);
3448     persp3d_update_box_reprs(persp);
3450     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3453 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3455     NULL, /* child_added */
3456     NULL, /* child_removed */
3457     box3d_persp_tb_event_attr_changed,
3458     NULL, /* content_changed */
3459     NULL  /* order_changed */
3460 };
3462 /**
3463  *  \param selection Should not be NULL.
3464  */
3465 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3466 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3467 static void box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3469     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3470     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3471     // update the perspectives with infinite VPs and leave the other ones untouched).
3473     Inkscape::XML::Node *persp_repr = NULL;
3474     purge_repr_listener(tbl, tbl);
3476     SPItem *item = selection->singleItem();
3477     if (item && SP_IS_BOX3D(item)) {
3478         // FIXME: Also deal with multiple selected boxes
3479         SPBox3D *box = SP_BOX3D(item);
3480         Persp3D *persp = box3d_get_perspective(box);
3481         persp_repr = SP_OBJECT_REPR(persp);
3482         if (persp_repr) {
3483             g_object_set_data(tbl, "repr", persp_repr);
3484             Inkscape::GC::anchor(persp_repr);
3485             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3486             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3487         }
3489         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3490         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3491         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3493         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3494         box3d_resync_toolbar(persp_repr, tbl);
3495         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3496     }
3499 static void box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3501     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3502     SPDocument *document = sp_desktop_document(desktop);
3504     // quit if run by the attr_changed or selection changed listener
3505     if (g_object_get_data( dataKludge, "freeze" )) {
3506         return;
3507     }
3509     // in turn, prevent listener from responding
3510     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3512     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3513     if (sel_persps.empty()) {
3514         // this can happen when the document is created; we silently ignore it
3515         return;
3516     }
3517     Persp3D *persp = sel_persps.front();
3519     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3520     SP_OBJECT(persp)->updateRepr();
3522     // TODO: use the correct axis here, too
3523     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3525     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3529 static void box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3531     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3534 static void box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3536     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3539 static void box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3541     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3545 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction * /*box3d_angle*/, Proj::Axis axis )
3547     // TODO: Take all selected perspectives into account
3548     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3549     if (sel_persps.empty()) {
3550         // this can happen when the document is created; we silently ignore it
3551         return;
3552     }
3553     Persp3D *persp = sel_persps.front();
3555     bool set_infinite = gtk_toggle_action_get_active(act);
3556     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3559 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3561     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3564 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3566     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3569 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3571     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3574 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3576     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3577     EgeAdjustmentAction* eact = 0;
3578     SPDocument *document = sp_desktop_document (desktop);
3579     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3581     EgeAdjustmentAction* box3d_angle_x = 0;
3582     EgeAdjustmentAction* box3d_angle_y = 0;
3583     EgeAdjustmentAction* box3d_angle_z = 0;
3585     /* Angle X */
3586     {
3587         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3588         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3589         eact = create_adjustment_action( "3DBoxAngleXAction",
3590                                          _("Angle in X direction"), _("Angle X:"),
3591                                          // Translators: PL is short for 'perspective line'
3592                                          _("Angle of PLs in X direction"),
3593                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3594                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3595                                          -360.0, 360.0, 1.0, 10.0,
3596                                          labels, values, G_N_ELEMENTS(labels),
3597                                          box3d_angle_x_value_changed );
3598         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3599         g_object_set_data( holder, "box3d_angle_x_action", eact );
3600         box3d_angle_x = eact;
3601     }
3603     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3604         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3605     } else {
3606         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3607     }
3610     /* VP X state */
3611     {
3612         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3613                                                       // Translators: VP is short for 'vanishing point'
3614                                                       _("State of VP in X direction"),
3615                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3616                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3617                                                       Inkscape::ICON_SIZE_DECORATION );
3618         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3619         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3620         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3621         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3622         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3623     }
3625     /* Angle Y */
3626     {
3627         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3628         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3629         eact = create_adjustment_action( "3DBoxAngleYAction",
3630                                          _("Angle in Y direction"), _("Angle Y:"),
3631                                          // Translators: PL is short for 'perspective line'
3632                                          _("Angle of PLs in Y direction"),
3633                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3634                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3635                                          -360.0, 360.0, 1.0, 10.0,
3636                                          labels, values, G_N_ELEMENTS(labels),
3637                                          box3d_angle_y_value_changed );
3638         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3639         g_object_set_data( holder, "box3d_angle_y_action", eact );
3640         box3d_angle_y = eact;
3641     }
3643     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3644         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3645     } else {
3646         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3647     }
3649     /* VP Y state */
3650     {
3651         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3652                                                       // Translators: VP is short for 'vanishing point'
3653                                                       _("State of VP in Y direction"),
3654                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3655                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3656                                                       Inkscape::ICON_SIZE_DECORATION );
3657         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3658         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3659         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3660         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3661         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3662     }
3664     /* Angle Z */
3665     {
3666         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3667         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3668         eact = create_adjustment_action( "3DBoxAngleZAction",
3669                                          _("Angle in Z direction"), _("Angle Z:"),
3670                                          // Translators: PL is short for 'perspective line'
3671                                          _("Angle of PLs in Z direction"),
3672                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3673                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3674                                          -360.0, 360.0, 1.0, 10.0,
3675                                          labels, values, G_N_ELEMENTS(labels),
3676                                          box3d_angle_z_value_changed );
3677         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3678         g_object_set_data( holder, "box3d_angle_z_action", eact );
3679         box3d_angle_z = eact;
3680     }
3682     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3683         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3684     } else {
3685         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3686     }
3688     /* VP Z state */
3689     {
3690         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3691                                                       // Translators: VP is short for 'vanishing point'
3692                                                       _("State of VP in Z direction"),
3693                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3694                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3695                                                       Inkscape::ICON_SIZE_DECORATION );
3696         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3697         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3698         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3699         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3700         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3701     }
3703     sigc::connection *connection = new sigc::connection(
3704         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3705        );
3706     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3707     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3710 //########################
3711 //##       Spiral       ##
3712 //########################
3714 static void sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3716     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3718     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3719         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3720         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3721     }
3723     // quit if run by the attr_changed listener
3724     if (g_object_get_data( tbl, "freeze" )) {
3725         return;
3726     }
3728     // in turn, prevent listener from responding
3729     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3731     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3733     bool modmade = false;
3734     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3735          items != NULL;
3736          items = items->next)
3737     {
3738         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3739             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3740             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3741             SP_OBJECT((SPItem *) items->data)->updateRepr();
3742             modmade = true;
3743         }
3744     }
3746     g_free(namespaced_name);
3748     if (modmade) {
3749         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3750                                    _("Change spiral"));
3751     }
3753     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3756 static void sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3758     sp_spl_tb_value_changed(adj, tbl, "revolution");
3761 static void sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3763     sp_spl_tb_value_changed(adj, tbl, "expansion");
3766 static void sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3768     sp_spl_tb_value_changed(adj, tbl, "t0");
3771 static void sp_spl_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
3773     GtkWidget *tbl = GTK_WIDGET(obj);
3775     GtkAdjustment *adj;
3777     // fixme: make settable
3778     gdouble rev = 5;
3779     gdouble exp = 1.0;
3780     gdouble t0 = 0.0;
3782     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3783     gtk_adjustment_set_value(adj, rev);
3784     gtk_adjustment_value_changed(adj);
3786     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3787     gtk_adjustment_set_value(adj, exp);
3788     gtk_adjustment_value_changed(adj);
3790     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3791     gtk_adjustment_set_value(adj, t0);
3792     gtk_adjustment_value_changed(adj);
3794     spinbutton_defocus(GTK_OBJECT(tbl));
3798 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr,
3799                                          gchar const * /*name*/,
3800                                          gchar const * /*old_value*/,
3801                                          gchar const * /*new_value*/,
3802                                          bool /*is_interactive*/,
3803                                          gpointer data)
3805     GtkWidget *tbl = GTK_WIDGET(data);
3807     // quit if run by the _changed callbacks
3808     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3809         return;
3810     }
3812     // in turn, prevent callbacks from responding
3813     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3815     GtkAdjustment *adj;
3816     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3817     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3819     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3820     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3822     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3823     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3825     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3829 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3830     NULL, /* child_added */
3831     NULL, /* child_removed */
3832     spiral_tb_event_attr_changed,
3833     NULL, /* content_changed */
3834     NULL  /* order_changed */
3835 };
3837 static void sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3839     int n_selected = 0;
3840     Inkscape::XML::Node *repr = NULL;
3842     purge_repr_listener( tbl, tbl );
3844     for (GSList const *items = selection->itemList();
3845          items != NULL;
3846          items = items->next)
3847     {
3848         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3849             n_selected++;
3850             repr = SP_OBJECT_REPR((SPItem *) items->data);
3851         }
3852     }
3854     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3856     if (n_selected == 0) {
3857         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3858     } else if (n_selected == 1) {
3859         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3861         if (repr) {
3862             g_object_set_data( tbl, "repr", repr );
3863             Inkscape::GC::anchor(repr);
3864             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3865             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3866         }
3867     } else {
3868         // FIXME: implement averaging of all parameters for multiple selected
3869         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3870         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3871     }
3875 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3877     EgeAdjustmentAction* eact = 0;
3878     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3880     {
3881         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3882         ege_output_action_set_use_markup( act, TRUE );
3883         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3884         g_object_set_data( holder, "mode_action", act );
3885     }
3887     /* Revolution */
3888     {
3889         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3890         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3891         eact = create_adjustment_action( "SpiralRevolutionAction",
3892                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3893                                          "/tools/shapes/spiral/revolution", 3.0,
3894                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3895                                          0.01, 1024.0, 0.1, 1.0,
3896                                          labels, values, G_N_ELEMENTS(labels),
3897                                          sp_spl_tb_revolution_value_changed, 1, 2);
3898         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3899     }
3901     /* Expansion */
3902     {
3903         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3904         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3905         eact = create_adjustment_action( "SpiralExpansionAction",
3906                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3907                                          "/tools/shapes/spiral/expansion", 1.0,
3908                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3909                                          0.0, 1000.0, 0.01, 1.0,
3910                                          labels, values, G_N_ELEMENTS(labels),
3911                                          sp_spl_tb_expansion_value_changed);
3912         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3913     }
3915     /* T0 */
3916     {
3917         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3918         gdouble values[] = {0, 0.5, 0.9};
3919         eact = create_adjustment_action( "SpiralT0Action",
3920                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3921                                          "/tools/shapes/spiral/t0", 0.0,
3922                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3923                                          0.0, 0.999, 0.01, 1.0,
3924                                          labels, values, G_N_ELEMENTS(labels),
3925                                          sp_spl_tb_t0_value_changed);
3926         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3927     }
3929     /* Reset */
3930     {
3931         InkAction* inky = ink_action_new( "SpiralResetAction",
3932                                           _("Defaults"),
3933                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3934                                           GTK_STOCK_CLEAR,
3935                                           secondarySize );
3936         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3937         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3938     }
3941     sigc::connection *connection = new sigc::connection(
3942         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3943         );
3944     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3945     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3948 //########################
3949 //##     Pen/Pencil     ##
3950 //########################
3952 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3953 static Glib::ustring const freehand_tool_name(GObject *dataKludge)
3955     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3956     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3957              ? "/tools/freehand/pen"
3958              : "/tools/freehand/pencil" );
3961 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3963     gint mode = ege_select_one_action_get_active(act);
3965     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3966     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3968     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3970     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3971     // preparatory work here
3972     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3973         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3974         sp_pen_context_set_polyline_mode(pc);
3975     }
3978 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3980     /* Freehand mode toggle buttons */
3981     {
3982         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3983         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3984         Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3986         {
3987             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3989             GtkTreeIter iter;
3990             gtk_list_store_append( model, &iter );
3991             gtk_list_store_set( model, &iter,
3992                                 0, _("Bezier"),
3993                                 1, _("Create regular Bezier path"),
3994                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3995                                 -1 );
3997             gtk_list_store_append( model, &iter );
3998             gtk_list_store_set( model, &iter,
3999                                 0, _("Spiro"),
4000                                 1, _("Create Spiro path"),
4001                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
4002                                 -1 );
4004             if (!tool_is_pencil) {
4005                 gtk_list_store_append( model, &iter );
4006                 gtk_list_store_set( model, &iter,
4007                                     0, _("Zigzag"),
4008                                     1, _("Create a sequence of straight line segments"),
4009                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
4010                                     -1 );
4012                 gtk_list_store_append( model, &iter );
4013                 gtk_list_store_set( model, &iter,
4014                                     0, _("Paraxial"),
4015                                     1, _("Create a sequence of paraxial line segments"),
4016                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
4017                                     -1 );
4018             }
4020             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
4021                                                                 "FreehandModeActionPencil" :
4022                                                                 "FreehandModeActionPen",
4023                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
4024             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4026             ege_select_one_action_set_appearance( act, "full" );
4027             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4028             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4029             ege_select_one_action_set_icon_column( act, 2 );
4030             ege_select_one_action_set_icon_size( act, secondarySize );
4031             ege_select_one_action_set_tooltip_column( act, 1  );
4033             ege_select_one_action_set_active( act, freehandMode);
4034             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
4035         }
4036     }
4039 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
4040     gint shape = ege_select_one_action_get_active( act );
4041     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4042     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
4045 /**
4046  * \brief Generate the list of freehand advanced shape option entries.
4047  */
4048 static GList * freehand_shape_dropdown_items_list() {
4049     GList *glist = NULL;
4051     glist = g_list_append (glist, _("None"));
4052     glist = g_list_append (glist, _("Triangle in"));
4053     glist = g_list_append (glist, _("Triangle out"));
4054     glist = g_list_append (glist, _("Ellipse"));
4055     glist = g_list_append (glist, _("From clipboard"));
4057     return glist;
4060 static void freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
4062     /*advanced shape options */
4063     {
4064         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4065         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4067         GList* items = 0;
4068         gint count = 0;
4069         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
4070         {
4071             GtkTreeIter iter;
4072             gtk_list_store_append( model, &iter );
4073             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4074             count++;
4075         }
4076         g_list_free( items );
4077         items = 0;
4078         EgeSelectOneAction* act1 = ege_select_one_action_new(
4079             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
4080             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
4081         g_object_set( act1, "short_label", _("Shape:"), NULL );
4082         ege_select_one_action_set_appearance( act1, "compact" );
4083         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
4084         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
4085         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4086         g_object_set_data( holder, "shape_action", act1 );
4087     }
4090 static void sp_pen_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4092     sp_add_freehand_mode_toggle(mainActions, holder, false);
4093     freehand_add_advanced_shape_options(mainActions, holder, false);
4097 static void sp_pencil_tb_defaults(GtkWidget * /*widget*/, GtkObject *obj)
4099     GtkWidget *tbl = GTK_WIDGET(obj);
4101     GtkAdjustment *adj;
4103     // fixme: make settable
4104     gdouble tolerance = 4;
4106     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
4107     gtk_adjustment_set_value(adj, tolerance);
4108     gtk_adjustment_value_changed(adj);
4110     spinbutton_defocus(GTK_OBJECT(tbl));
4113 static void sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
4115     // quit if run by the attr_changed listener
4116     if (g_object_get_data( tbl, "freeze" )) {
4117         return;
4118     }
4119     // in turn, prevent listener from responding
4120     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4121     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4122     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4123     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4126 /*
4127 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4128 public:
4129     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4130     {
4131         g_object_set_data(_obj, "prefobserver", this);
4132     }
4133     virtual ~PencilToleranceObserver() {
4134         if (g_object_get_data(_obj, "prefobserver") == this) {
4135             g_object_set_data(_obj, "prefobserver", NULL);
4136         }
4137     }
4138     virtual void notify(Inkscape::Preferences::Entry const &val) {
4139         GObject* tbl = _obj;
4140         if (g_object_get_data( tbl, "freeze" )) {
4141             return;
4142         }
4143         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4145         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4147         double v = val.getDouble(adj->value);
4148         gtk_adjustment_set_value(adj, v);
4149         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4150     }
4151 private:
4152     GObject *_obj;
4153 };
4154 */
4156 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4158     sp_add_freehand_mode_toggle(mainActions, holder, true);
4160     EgeAdjustmentAction* eact = 0;
4162     /* Tolerance */
4163     {
4164         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4165         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4166         eact = create_adjustment_action( "PencilToleranceAction",
4167                                          _("Smoothing:"), _("Smoothing: "),
4168                  _("How much smoothing (simplifying) is applied to the line"),
4169                                          "/tools/freehand/pencil/tolerance",
4170                                          3.0,
4171                                          GTK_WIDGET(desktop->canvas), NULL,
4172                                          holder, TRUE, "altx-pencil",
4173                                          1, 100.0, 0.5, 1.0,
4174                                          labels, values, G_N_ELEMENTS(labels),
4175                                          sp_pencil_tb_tolerance_value_changed,
4176                                          1, 2);
4177         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4178         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4179     }
4181     /* advanced shape options */
4182     freehand_add_advanced_shape_options(mainActions, holder, true);
4184     /* Reset */
4185     {
4186         InkAction* inky = ink_action_new( "PencilResetAction",
4187                                           _("Defaults"),
4188                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4189                                           GTK_STOCK_CLEAR,
4190                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4191         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4192         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4193     }
4195     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4200 //########################
4201 //##       Tweak        ##
4202 //########################
4204 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4206     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4207     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4210 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4212     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4213     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4216 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4218     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4219     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4222 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4224     int mode = ege_select_one_action_get_active( act );
4225     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4226     prefs->setInt("/tools/tweak/mode", mode);
4228     static gchar const* names[] = {"tweak_doh", "tweak_dos", "tweak_dol", "tweak_doo", "tweak_channels_label"};
4229     bool flag = ((mode == TWEAK_MODE_COLORPAINT) || (mode == TWEAK_MODE_COLORJITTER));
4230     for (size_t i = 0; i < G_N_ELEMENTS(names); ++i) {
4231         GtkAction *act = GTK_ACTION(g_object_get_data( tbl, names[i] ));
4232         if (act) {
4233             gtk_action_set_sensitive(act, flag);
4234         }
4235     }
4236     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4237     if (fid) {
4238         gtk_action_set_sensitive(fid, !flag);
4239     }
4242 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4244     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4245     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4248 static void tweak_toggle_doh(GtkToggleAction *act, gpointer /*data*/) {
4249     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4250     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4252 static void tweak_toggle_dos(GtkToggleAction *act, gpointer /*data*/) {
4253     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4254     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4256 static void tweak_toggle_dol(GtkToggleAction *act, gpointer /*data*/) {
4257     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4258     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4260 static void tweak_toggle_doo(GtkToggleAction *act, gpointer /*data*/) {
4261     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4262     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4265 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4267     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4268     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4270     {
4271         /* Width */
4272         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4273         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4274         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4275                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4276                                                               "/tools/tweak/width", 15,
4277                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4278                                                               1, 100, 1.0, 10.0,
4279                                                               labels, values, G_N_ELEMENTS(labels),
4280                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4281         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4282         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4283         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4284     }
4287     {
4288         /* Force */
4289         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4290         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4291         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4292                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4293                                                               "/tools/tweak/force", 20,
4294                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4295                                                               1, 100, 1.0, 10.0,
4296                                                               labels, values, G_N_ELEMENTS(labels),
4297                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4298         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4299         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4300         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4301     }
4303     /* Mode */
4304     {
4305         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4307         GtkTreeIter iter;
4308         gtk_list_store_append( model, &iter );
4309         gtk_list_store_set( model, &iter,
4310                             0, _("Move mode"),
4311                             1, _("Move objects in any direction"),
4312                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4313                             -1 );
4315         gtk_list_store_append( model, &iter );
4316         gtk_list_store_set( model, &iter,
4317                             0, _("Move in/out mode"),
4318                             1, _("Move objects towards cursor; with Shift from cursor"),
4319                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4320                             -1 );
4322         gtk_list_store_append( model, &iter );
4323         gtk_list_store_set( model, &iter,
4324                             0, _("Move jitter mode"),
4325                             1, _("Move objects in random directions"),
4326                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4327                             -1 );
4329         gtk_list_store_append( model, &iter );
4330         gtk_list_store_set( model, &iter,
4331                             0, _("Scale mode"),
4332                             1, _("Shrink objects, with Shift enlarge"),
4333                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4334                             -1 );
4336         gtk_list_store_append( model, &iter );
4337         gtk_list_store_set( model, &iter,
4338                             0, _("Rotate mode"),
4339                             1, _("Rotate objects, with Shift counterclockwise"),
4340                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4341                             -1 );
4343         gtk_list_store_append( model, &iter );
4344         gtk_list_store_set( model, &iter,
4345                             0, _("Duplicate/delete mode"),
4346                             1, _("Duplicate objects, with Shift delete"),
4347                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4348                             -1 );
4350         gtk_list_store_append( model, &iter );
4351         gtk_list_store_set( model, &iter,
4352                             0, _("Push mode"),
4353                             1, _("Push parts of paths in any direction"),
4354                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4355                             -1 );
4357         gtk_list_store_append( model, &iter );
4358         gtk_list_store_set( model, &iter,
4359                             0, _("Shrink/grow mode"),
4360                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4361                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4362                             -1 );
4364         gtk_list_store_append( model, &iter );
4365         gtk_list_store_set( model, &iter,
4366                             0, _("Attract/repel mode"),
4367                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4368                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4369                             -1 );
4371         gtk_list_store_append( model, &iter );
4372         gtk_list_store_set( model, &iter,
4373                             0, _("Roughen mode"),
4374                             1, _("Roughen parts of paths"),
4375                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4376                             -1 );
4378         gtk_list_store_append( model, &iter );
4379         gtk_list_store_set( model, &iter,
4380                             0, _("Color paint mode"),
4381                             1, _("Paint the tool's color upon selected objects"),
4382                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4383                             -1 );
4385         gtk_list_store_append( model, &iter );
4386         gtk_list_store_set( model, &iter,
4387                             0, _("Color jitter mode"),
4388                             1, _("Jitter the colors of selected objects"),
4389                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4390                             -1 );
4392         gtk_list_store_append( model, &iter );
4393         gtk_list_store_set( model, &iter,
4394                             0, _("Blur mode"),
4395                             1, _("Blur selected objects more; with Shift, blur less"),
4396                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4397                             -1 );
4400         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4401         g_object_set( act, "short_label", _("Mode:"), NULL );
4402         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4403         g_object_set_data( holder, "mode_action", act );
4405         ege_select_one_action_set_appearance( act, "full" );
4406         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4407         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4408         ege_select_one_action_set_icon_column( act, 2 );
4409         ege_select_one_action_set_icon_size( act, secondarySize );
4410         ege_select_one_action_set_tooltip_column( act, 1  );
4412         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4413         ege_select_one_action_set_active( act, mode );
4414         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4416         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4417     }
4419     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4421     {
4422         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4423         ege_output_action_set_use_markup( act, TRUE );
4424         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4425         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4426             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4427         }
4428         g_object_set_data( holder, "tweak_channels_label", act);
4429     }
4431     {
4432         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4433                                                       _("Hue"),
4434                                                       _("In color mode, act on objects' hue"),
4435                                                       NULL,
4436                                                       Inkscape::ICON_SIZE_DECORATION );
4437         //TRANSLATORS:  "H" here stands for hue
4438         g_object_set( act, "short_label", _("H"), NULL );
4439         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4440         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4441         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4442         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4443             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4444         }
4445         g_object_set_data( holder, "tweak_doh", act);
4446     }
4447     {
4448         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4449                                                       _("Saturation"),
4450                                                       _("In color mode, act on objects' saturation"),
4451                                                       NULL,
4452                                                       Inkscape::ICON_SIZE_DECORATION );
4453         //TRANSLATORS: "S" here stands for Saturation
4454         g_object_set( act, "short_label", _("S"), NULL );
4455         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4456         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4457         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4458         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4459             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4460         }
4461         g_object_set_data( holder, "tweak_dos", act );
4462     }
4463     {
4464         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4465                                                       _("Lightness"),
4466                                                       _("In color mode, act on objects' lightness"),
4467                                                       NULL,
4468                                                       Inkscape::ICON_SIZE_DECORATION );
4469         //TRANSLATORS: "L" here stands for Lightness
4470         g_object_set( act, "short_label", _("L"), NULL );
4471         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4472         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4473         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4474         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4475             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4476         }
4477         g_object_set_data( holder, "tweak_dol", act );
4478     }
4479     {
4480         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4481                                                       _("Opacity"),
4482                                                       _("In color mode, act on objects' opacity"),
4483                                                       NULL,
4484                                                       Inkscape::ICON_SIZE_DECORATION );
4485         //TRANSLATORS: "O" here stands for Opacity
4486         g_object_set( act, "short_label", _("O"), NULL );
4487         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4488         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4489         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4490         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER) {
4491             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4492         }
4493         g_object_set_data( holder, "tweak_doo", act );
4494     }
4496     {   /* Fidelity */
4497         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4498         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4499         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4500                                                               _("Fidelity"), _("Fidelity:"),
4501                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4502                                                               "/tools/tweak/fidelity", 50,
4503                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4504                                                               1, 100, 1.0, 10.0,
4505                                                               labels, values, G_N_ELEMENTS(labels),
4506                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4507         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4508         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4509         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4510             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4511         }
4512         g_object_set_data( holder, "tweak_fidelity", eact );
4513     }
4516     /* Use Pressure button */
4517     {
4518         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4519                                                       _("Pressure"),
4520                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4521                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4522                                                       Inkscape::ICON_SIZE_DECORATION );
4523         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4524         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4525         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4526     }
4531 //########################
4532 //##       Spray        ##
4533 //########################
4535 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4537     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4538     prefs->setDouble( "/tools/spray/width", adj->value );
4541 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4543     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4544     prefs->setDouble( "/tools/spray/mean", adj->value );
4547 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4549     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4550     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4553 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4555     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4556     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4559 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject * /*tbl*/ )
4561     int mode = ege_select_one_action_get_active( act );
4562     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4563     prefs->setInt("/tools/spray/mode", mode);
4566 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4568     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4569     prefs->setDouble( "/tools/spray/population", adj->value );
4572 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4574     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4575     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4578 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject * /*tbl*/ )
4580     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4581     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4585 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4587     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4588     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4590     {
4591         /* Width */
4592         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4593         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4594         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4595                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4596                                                               "/tools/spray/width", 15,
4597                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4598                                                               1, 100, 1.0, 10.0,
4599                                                               labels, values, G_N_ELEMENTS(labels),
4600                                                               sp_spray_width_value_changed,  1, 0 );
4601         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4602         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4603         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4604     }
4606     {
4607         /* Mean */
4608         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4609         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4610         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4611                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4612                                                               "/tools/spray/mean", 0,
4613                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4614                                                               0, 100, 1.0, 10.0,
4615                                                               labels, values, G_N_ELEMENTS(labels),
4616                                                               sp_spray_mean_value_changed,  1, 0 );
4617         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4618         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4619         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4620     }
4622     {
4623         /* Standard_deviation */
4624         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4625         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4626         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4627                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4628                                                               "/tools/spray/standard_deviation", 70,
4629                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4630                                                               1, 100, 1.0, 10.0,
4631                                                               labels, values, G_N_ELEMENTS(labels),
4632                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4633         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4634         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4635         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4636     }
4638     /* Mode */
4639     {
4640         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4642         GtkTreeIter iter;
4643         gtk_list_store_append( model, &iter );
4644         gtk_list_store_set( model, &iter,
4645                             0, _("Spray with copies"),
4646                             1, _("Spray copies of the initial selection"),
4647                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4648                             -1 );
4650         gtk_list_store_append( model, &iter );
4651         gtk_list_store_set( model, &iter,
4652                             0, _("Spray with clones"),
4653                             1, _("Spray clones of the initial selection"),
4654                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4655                             -1 );
4657         gtk_list_store_append( model, &iter );
4658         gtk_list_store_set( model, &iter,
4659                             0, _("Spray single path"),
4660                             1, _("Spray objects in a single path"),
4661                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4662                             -1 );
4664         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4665         g_object_set( act, "short_label", _("Mode:"), NULL );
4666         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4667         g_object_set_data( holder, "mode_action", act );
4669         ege_select_one_action_set_appearance( act, "full" );
4670         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4671         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4672         ege_select_one_action_set_icon_column( act, 2 );
4673         ege_select_one_action_set_icon_size( act, secondarySize );
4674         ege_select_one_action_set_tooltip_column( act, 1  );
4676         gint mode = prefs->getInt("/tools/spray/mode", 1);
4677         ege_select_one_action_set_active( act, mode );
4678         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4680         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4681     }
4683     {   /* Population */
4684         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4685         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4686         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4687                                                               _("Amount"), _("Amount:"),
4688                                                               _("Adjusts the number of items sprayed per clic."),
4689                                                               "/tools/spray/population", 70,
4690                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4691                                                               1, 100, 1.0, 10.0,
4692                                                               labels, values, G_N_ELEMENTS(labels),
4693                                                               sp_spray_population_value_changed,  1, 0 );
4694         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4695         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4696         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4697         g_object_set_data( holder, "spray_population", eact );
4698     }
4700     /* Use Pressure button */
4701     {
4702         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4703                                                       _("Pressure"),
4704                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4705                                                       "use_pressure",
4706                                                       Inkscape::ICON_SIZE_DECORATION );
4707         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4708         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4709         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4710     }
4712     {   /* Rotation */
4713         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4714         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4715         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4716                                                               _("Rotation"), _("Rotation:"),
4717                                                               // xgettext:no-c-format
4718                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4719                                                               "/tools/spray/rotation_variation", 0,
4720                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4721                                                               0, 100, 1.0, 10.0,
4722                                                               labels, values, G_N_ELEMENTS(labels),
4723                                                               sp_spray_rotation_value_changed,  1, 0 );
4724         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4725         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4726         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4727         g_object_set_data( holder, "spray_rotation", eact );
4728     }
4730     {   /* Scale */
4731         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4732         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4733         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4734                                                               _("Scale"), _("Scale:"),
4735                                                               // xgettext:no-c-format
4736                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4737                                                               "/tools/spray/scale_variation", 0,
4738                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4739                                                               0, 100, 1.0, 10.0,
4740                                                               labels, values, G_N_ELEMENTS(labels),
4741                                                               sp_spray_scale_value_changed,  1, 0 );
4742         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4743         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4744         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4745         g_object_set_data( holder, "spray_scale", eact );
4746     }
4753 //########################
4754 //##     Calligraphy    ##
4755 //########################
4756 static void update_presets_list(GObject *tbl)
4758     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4759     if (g_object_get_data(tbl, "presets_blocked")) {
4760         return;
4761     }
4763     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4764     if (!sel) {
4765         // WTF!? This will cause a segfault if ever reached
4766         //ege_select_one_action_set_active(sel, 0);
4767         return;
4768     }
4770     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4772     int ege_index = 1;
4773     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4774         bool match = true;
4776         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4777         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4778             Glib::ustring entry_name = j->getEntryName();
4779             if (entry_name == "id" || entry_name == "name") {
4780                 continue;
4781             }
4783             void *widget = g_object_get_data(tbl, entry_name.data());
4784             if (widget) {
4785                 if (GTK_IS_ADJUSTMENT(widget)) {
4786                     double v = j->getDouble();
4787                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4788                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4789                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4790                         match = false;
4791                         break;
4792                     }
4793                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4794                     bool v = j->getBool();
4795                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4796                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4797                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4798                         match = false;
4799                         break;
4800                     }
4801                 }
4802             }
4803         }
4805         if (match) {
4806             // newly added item is at the same index as the
4807             // save command, so we need to change twice for it to take effect
4808             ege_select_one_action_set_active(sel, 0);
4809             ege_select_one_action_set_active(sel, ege_index); // one-based index
4810             return;
4811         }
4812     }
4814     // no match found
4815     ege_select_one_action_set_active(sel, 0);
4818 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4820     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4821     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4822     update_presets_list(tbl);
4825 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4827     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4828     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4829     update_presets_list(tbl);
4832 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4834     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4835     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4836     update_presets_list(tbl);
4839 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4841     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4842     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4843     update_presets_list(tbl);
4846 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4848     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4849     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4850     update_presets_list(tbl);
4853 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4855     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4856     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4857     update_presets_list(tbl);
4860 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4862     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4863     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4864     update_presets_list(tbl);
4867 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4869     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4870     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4871     update_presets_list(tbl);
4874 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4876     // TODO merge into PrefPusher
4877     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4878     if (calligraphy_angle ) {
4879         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4880     }
4884 static gchar const *const widget_names[] = {
4885     "width",
4886     "mass",
4887     "wiggle",
4888     "angle",
4889     "thinning",
4890     "tremor",
4891     "flatness",
4892     "cap_rounding",
4893     "usepressure",
4894     "tracebackground",
4895     "usetilt"
4896 };
4899 static void sp_dcc_build_presets_list(GObject *tbl)
4901     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4903     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4904     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4905     gtk_list_store_clear (model);
4907     {
4908         GtkTreeIter iter;
4909         gtk_list_store_append( model, &iter );
4910         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4911     }
4913     // iterate over all presets to populate the list
4914     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4915     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4916     int ii=1;
4918     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4919         GtkTreeIter iter;
4920         Glib::ustring preset_name = prefs->getString(*i + "/name");
4921         gtk_list_store_append( model, &iter );
4922         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4923     }
4925     {
4926         GtkTreeIter iter;
4927         gtk_list_store_append( model, &iter );
4928         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4929         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4930     }
4932     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4934     update_presets_list (tbl);
4937 static void sp_dcc_save_profile(GtkWidget * /*widget*/, GObject *tbl)
4939     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4940     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4941     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4942     if (! desktop) {
4943         return;
4944     }
4946     if (g_object_get_data(tbl, "presets_blocked")) {
4947         return;
4948     }
4950     CalligraphicProfileRename::show(desktop);
4951     if ( !CalligraphicProfileRename::applied()) {
4952         // dialog cancelled
4953         update_presets_list (tbl);
4954         return;
4955     }
4956     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4958     if (profile_name.empty()) {
4959         // empty name entered
4960         update_presets_list (tbl);
4961         return;
4962     }
4964     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4966     // If there's a preset with the given name, find it and set save_path appropriately
4967     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4968     int total_presets = presets.size();
4969     int new_index = -1;
4970     Glib::ustring save_path; // profile pref path without a trailing slash
4972     int temp_index = 0;
4973     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4974         Glib::ustring name = prefs->getString(*i + "/name");
4975         if (!name.empty() && profile_name == name) {
4976             new_index = temp_index;
4977             save_path = *i;
4978             break;
4979         }
4980     }
4982     if (new_index == -1) {
4983         // no preset with this name, create
4984         new_index = total_presets + 1;
4985         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4986         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4987         g_free(profile_id);
4988     }
4990     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4991         gchar const *const widget_name = widget_names[i];
4992         void *widget = g_object_get_data(tbl, widget_name);
4993         if (widget) {
4994             if (GTK_IS_ADJUSTMENT(widget)) {
4995                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4996                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4997                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4998             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4999                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5000                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
5001                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
5002             } else {
5003                 g_warning("Unknown widget type for preset: %s\n", widget_name);
5004             }
5005         } else {
5006             g_warning("Bad key when writing preset: %s\n", widget_name);
5007         }
5008     }
5009     prefs->setString(save_path + "/name", profile_name);
5011     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5012     sp_dcc_build_presets_list (tbl);
5016 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl)
5018     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5020     gint preset_index = ege_select_one_action_get_active( act );
5021     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
5022     // even when the preset is not changed. It would be good to replace it with something more
5023     // modern. Index 0 means "No preset", so we don't do anything.
5024     if (preset_index == 0) {
5025         return;
5026     }
5028     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
5030     if (preset_index == save_presets_index) {
5031         // this is the Save command
5032         sp_dcc_save_profile(NULL, tbl);
5033         return;
5034     }
5036     if (g_object_get_data(tbl, "presets_blocked")) {
5037         return;
5038     }
5040     // preset_index is one-based so we subtract 1
5041     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
5042     Glib::ustring preset_path = presets.at(preset_index - 1);
5044     if (!preset_path.empty()) {
5045         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
5047         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
5049         // Shouldn't this be std::map?
5050         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
5051             Glib::ustring entry_name = i->getEntryName();
5052             if (entry_name == "id" || entry_name == "name") {
5053                 continue;
5054             }
5055             void *widget = g_object_get_data(tbl, entry_name.data());
5056             if (widget) {
5057                 if (GTK_IS_ADJUSTMENT(widget)) {
5058                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
5059                     gtk_adjustment_set_value(adj, i->getDouble());
5060                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
5061                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
5062                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5063                     gtk_toggle_action_set_active(toggle, i->getBool());
5064                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
5065                 } else {
5066                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
5067                 }
5068             } else {
5069                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
5070             }
5071         }
5072         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5073     }
5076 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5078     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5079     {
5080         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
5082         EgeAdjustmentAction* calligraphy_angle = 0;
5084         {
5085         /* Width */
5086         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5087         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5088         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
5089                                                               _("Pen Width"), _("Width:"),
5090                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
5091                                                               "/tools/calligraphic/width", 15,
5092                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
5093                                                               1, 100, 1.0, 10.0,
5094                                                               labels, values, G_N_ELEMENTS(labels),
5095                                                               sp_ddc_width_value_changed,  1, 0 );
5096         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5097         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5098         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5099         }
5101         {
5102         /* Thinning */
5103             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
5104             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
5105         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
5106                                                               _("Stroke Thinning"), _("Thinning:"),
5107                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
5108                                                               "/tools/calligraphic/thinning", 10,
5109                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5110                                                               -100, 100, 1, 10.0,
5111                                                               labels, values, G_N_ELEMENTS(labels),
5112                                                               sp_ddc_velthin_value_changed, 1, 0);
5113         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5114         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5115         }
5117         {
5118         /* Angle */
5119         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5120         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5121         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5122                                                               _("Pen Angle"), _("Angle:"),
5123                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5124                                                               "/tools/calligraphic/angle", 30,
5125                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5126                                                               -90.0, 90.0, 1.0, 10.0,
5127                                                               labels, values, G_N_ELEMENTS(labels),
5128                                                               sp_ddc_angle_value_changed, 1, 0 );
5129         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5130         g_object_set_data( holder, "angle_action", eact );
5131         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5132         calligraphy_angle = eact;
5133         }
5135         {
5136         /* Fixation */
5137             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5138         gdouble values[] = {0, 20, 40, 60, 90, 100};
5139         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5140                                                               _("Fixation"), _("Fixation:"),
5141                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5142                                                               "/tools/calligraphic/flatness", 90,
5143                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5144                                                               0.0, 100, 1.0, 10.0,
5145                                                               labels, values, G_N_ELEMENTS(labels),
5146                                                               sp_ddc_flatness_value_changed, 1, 0);
5147         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5148         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5149         }
5151         {
5152         /* Cap Rounding */
5153             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5154         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5155         // TRANSLATORS: "cap" means "end" (both start and finish) here
5156         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5157                                                               _("Cap rounding"), _("Caps:"),
5158                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5159                                                               "/tools/calligraphic/cap_rounding", 0.0,
5160                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5161                                                               0.0, 5.0, 0.01, 0.1,
5162                                                               labels, values, G_N_ELEMENTS(labels),
5163                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5164         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5165         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5166         }
5168         {
5169         /* Tremor */
5170             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5171         gdouble values[] = {0, 10, 20, 40, 60, 100};
5172         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5173                                                               _("Stroke Tremor"), _("Tremor:"),
5174                                                               _("Increase to make strokes rugged and trembling"),
5175                                                               "/tools/calligraphic/tremor", 0.0,
5176                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5177                                                               0.0, 100, 1, 10.0,
5178                                                               labels, values, G_N_ELEMENTS(labels),
5179                                                               sp_ddc_tremor_value_changed, 1, 0);
5181         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5182         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5183         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5184         }
5186         {
5187         /* Wiggle */
5188         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5189         gdouble values[] = {0, 20, 40, 60, 100};
5190         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5191                                                               _("Pen Wiggle"), _("Wiggle:"),
5192                                                               _("Increase to make the pen waver and wiggle"),
5193                                                               "/tools/calligraphic/wiggle", 0.0,
5194                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5195                                                               0.0, 100, 1, 10.0,
5196                                                               labels, values, G_N_ELEMENTS(labels),
5197                                                               sp_ddc_wiggle_value_changed, 1, 0);
5198         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5199         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5200         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5201         }
5203         {
5204         /* Mass */
5205             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5206         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5207         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5208                                                               _("Pen Mass"), _("Mass:"),
5209                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5210                                                               "/tools/calligraphic/mass", 2.0,
5211                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5212                                                               0.0, 100, 1, 10.0,
5213                                                               labels, values, G_N_ELEMENTS(labels),
5214                                                               sp_ddc_mass_value_changed, 1, 0);
5215         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5216         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5217         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5218         }
5221         /* Trace Background button */
5222         {
5223             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5224                                                           _("Trace Background"),
5225                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5226                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5227                                                           Inkscape::ICON_SIZE_DECORATION );
5228             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5229             PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/tracebackground", update_presets_list, holder);
5230             g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5231             g_object_set_data( holder, "tracebackground", act );
5232         }
5234         /* Use Pressure button */
5235         {
5236             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5237                                                           _("Pressure"),
5238                                                           _("Use the pressure of the input device to alter the width of the pen"),
5239                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5240                                                           Inkscape::ICON_SIZE_DECORATION );
5241             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5242             PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usepressure", update_presets_list, holder);
5243             g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5244             g_object_set_data( holder, "usepressure", act );
5245         }
5247         /* Use Tilt button */
5248         {
5249             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5250                                                           _("Tilt"),
5251                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5252                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5253                                                           Inkscape::ICON_SIZE_DECORATION );
5254             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5255             PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/calligraphic/usetilt", update_presets_list, holder);
5256             g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
5257             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5258             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5259             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5260             g_object_set_data( holder, "usetilt", act );
5261         }
5263         /*calligraphic profile */
5264         {
5265             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5266             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5267             ege_select_one_action_set_appearance (act1, "compact");
5268             g_object_set_data (holder, "profile_selector", act1 );
5270             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5272             sp_dcc_build_presets_list (holder);
5274             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5275             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5276         }
5277     }
5281 //########################
5282 //##    Circle / Arc    ##
5283 //########################
5285 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5287     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5288     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5290     if (v1 == 0 && v2 == 0) {
5291         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5292             gtk_action_set_sensitive( ocb, FALSE );
5293             gtk_action_set_sensitive( make_whole, FALSE );
5294         }
5295     } else {
5296         gtk_action_set_sensitive( ocb, TRUE );
5297         gtk_action_set_sensitive( make_whole, TRUE );
5298     }
5301 static void
5302 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5304     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5306     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5307         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5308         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5309     }
5311     // quit if run by the attr_changed listener
5312     if (g_object_get_data( tbl, "freeze" )) {
5313         return;
5314     }
5316     // in turn, prevent listener from responding
5317     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5319     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5321     bool modmade = false;
5322     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5323          items != NULL;
5324          items = items->next)
5325     {
5326         SPItem *item = SP_ITEM(items->data);
5328         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5330             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5331             SPArc *arc = SP_ARC(item);
5333             if (!strcmp(value_name, "start")) {
5334                 ge->start = (adj->value * M_PI)/ 180;
5335             } else {
5336                 ge->end = (adj->value * M_PI)/ 180;
5337             }
5339             sp_genericellipse_normalize(ge);
5340             ((SPObject *)arc)->updateRepr();
5341             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5343             modmade = true;
5344         }
5345     }
5347     g_free(namespaced_name);
5349     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5351     sp_arctb_sensitivize( tbl, adj->value, other->value );
5353     if (modmade) {
5354         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5355                                    _("Arc: Change start/end"));
5356     }
5358     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5362 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5364     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5367 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5369     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5373 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5375     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5376     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5377         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5378         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5379     }
5381     // quit if run by the attr_changed listener
5382     if (g_object_get_data( tbl, "freeze" )) {
5383         return;
5384     }
5386     // in turn, prevent listener from responding
5387     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5389     bool modmade = false;
5391     if ( ege_select_one_action_get_active(act) != 0 ) {
5392         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5393              items != NULL;
5394              items = items->next)
5395         {
5396             if (SP_IS_ARC((SPItem *) items->data)) {
5397                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5398                 repr->setAttribute("sodipodi:open", "true");
5399                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5400                 modmade = true;
5401             }
5402         }
5403     } else {
5404         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5405              items != NULL;
5406              items = items->next)
5407         {
5408             if (SP_IS_ARC((SPItem *) items->data)) {
5409                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5410                 repr->setAttribute("sodipodi:open", NULL);
5411                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5412                 modmade = true;
5413             }
5414         }
5415     }
5417     if (modmade) {
5418         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5419                                    _("Arc: Change open/closed"));
5420     }
5422     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5425 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5427     GtkAdjustment *adj;
5428     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5429     gtk_adjustment_set_value(adj, 0.0);
5430     gtk_adjustment_value_changed(adj);
5432     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5433     gtk_adjustment_set_value(adj, 0.0);
5434     gtk_adjustment_value_changed(adj);
5436     spinbutton_defocus( GTK_OBJECT(obj) );
5439 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const * /*name*/,
5440                                       gchar const * /*old_value*/, gchar const * /*new_value*/,
5441                                       bool /*is_interactive*/, gpointer data)
5443     GObject *tbl = G_OBJECT(data);
5445     // quit if run by the _changed callbacks
5446     if (g_object_get_data( tbl, "freeze" )) {
5447         return;
5448     }
5450     // in turn, prevent callbacks from responding
5451     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5453     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5454     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5456     GtkAdjustment *adj1,*adj2;
5457     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5458     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5459     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5460     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5462     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5464     char const *openstr = NULL;
5465     openstr = repr->attribute("sodipodi:open");
5466     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5468     if (openstr) {
5469         ege_select_one_action_set_active( ocb, 1 );
5470     } else {
5471         ege_select_one_action_set_active( ocb, 0 );
5472     }
5474     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5477 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5478     NULL, /* child_added */
5479     NULL, /* child_removed */
5480     arc_tb_event_attr_changed,
5481     NULL, /* content_changed */
5482     NULL  /* order_changed */
5483 };
5486 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5488     int n_selected = 0;
5489     Inkscape::XML::Node *repr = NULL;
5491     purge_repr_listener( tbl, tbl );
5493     for (GSList const *items = selection->itemList();
5494          items != NULL;
5495          items = items->next)
5496     {
5497         if (SP_IS_ARC((SPItem *) items->data)) {
5498             n_selected++;
5499             repr = SP_OBJECT_REPR((SPItem *) items->data);
5500         }
5501     }
5503     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5505     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5506     if (n_selected == 0) {
5507         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5508     } else if (n_selected == 1) {
5509         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5510         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5512         if (repr) {
5513             g_object_set_data( tbl, "repr", repr );
5514             Inkscape::GC::anchor(repr);
5515             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5516             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5517         }
5518     } else {
5519         // FIXME: implement averaging of all parameters for multiple selected
5520         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5521         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5522         sp_arctb_sensitivize( tbl, 1, 0 );
5523     }
5527 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5529     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5531     EgeAdjustmentAction* eact = 0;
5532     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5535     {
5536         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5537         ege_output_action_set_use_markup( act, TRUE );
5538         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5539         g_object_set_data( holder, "mode_action", act );
5540     }
5542     /* Start */
5543     {
5544         eact = create_adjustment_action( "ArcStartAction",
5545                                          _("Start"), _("Start:"),
5546                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5547                                          "/tools/shapes/arc/start", 0.0,
5548                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5549                                          -360.0, 360.0, 1.0, 10.0,
5550                                          0, 0, 0,
5551                                          sp_arctb_start_value_changed);
5552         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5553     }
5555     /* End */
5556     {
5557         eact = create_adjustment_action( "ArcEndAction",
5558                                          _("End"), _("End:"),
5559                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5560                                          "/tools/shapes/arc/end", 0.0,
5561                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5562                                          -360.0, 360.0, 1.0, 10.0,
5563                                          0, 0, 0,
5564                                          sp_arctb_end_value_changed);
5565         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5566     }
5568     /* Segments / Pie checkbox */
5569     {
5570         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5572         GtkTreeIter iter;
5573         gtk_list_store_append( model, &iter );
5574         gtk_list_store_set( model, &iter,
5575                             0, _("Closed arc"),
5576                             1, _("Switch to segment (closed shape with two radii)"),
5577                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5578                             -1 );
5580         gtk_list_store_append( model, &iter );
5581         gtk_list_store_set( model, &iter,
5582                             0, _("Open Arc"),
5583                             1, _("Switch to arc (unclosed shape)"),
5584                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5585                             -1 );
5587         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5588         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5589         g_object_set_data( holder, "open_action", act );
5591         ege_select_one_action_set_appearance( act, "full" );
5592         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5593         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5594         ege_select_one_action_set_icon_column( act, 2 );
5595         ege_select_one_action_set_icon_size( act, secondarySize );
5596         ege_select_one_action_set_tooltip_column( act, 1  );
5598         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5599         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5600         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5601     }
5603     /* Make Whole */
5604     {
5605         InkAction* inky = ink_action_new( "ArcResetAction",
5606                                           _("Make whole"),
5607                                           _("Make the shape a whole ellipse, not arc or segment"),
5608                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5609                                           secondarySize );
5610         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5611         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5612         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5613         g_object_set_data( holder, "make_whole", inky );
5614     }
5616     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5617     // sensitivize make whole and open checkbox
5618     {
5619         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5620         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5621         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5622     }
5625     sigc::connection *connection = new sigc::connection(
5626         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5627         );
5628     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5629     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5635 // toggle button callbacks and updaters
5637 //########################
5638 //##      Dropper       ##
5639 //########################
5641 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl )
5643     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5644     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5645     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5646     if ( set_action ) {
5647         if ( gtk_toggle_action_get_active( act ) ) {
5648             gtk_action_set_sensitive( set_action, TRUE );
5649         } else {
5650             gtk_action_set_sensitive( set_action, FALSE );
5651         }
5652     }
5654     spinbutton_defocus(GTK_OBJECT(tbl));
5657 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl )
5659     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5660     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5661     spinbutton_defocus(GTK_OBJECT(tbl));
5665 /**
5666  * Dropper auxiliary toolbar construction and setup.
5667  *
5668  * TODO: Would like to add swatch of current color.
5669  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5670  *       can drag and drop places. Will provide a nice mixing palette.
5671  */
5672 static void sp_dropper_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5674     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5675     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5677     {
5678         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5679         ege_output_action_set_use_markup( act, TRUE );
5680         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5681     }
5683     {
5684         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5685                                                       _("Pick opacity"),
5686                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5687                                                       NULL,
5688                                                       Inkscape::ICON_SIZE_DECORATION );
5689         g_object_set( act, "short_label", _("Pick"), NULL );
5690         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5691         g_object_set_data( holder, "pick_action", act );
5692         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5693         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5694     }
5696     {
5697         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5698                                                       _("Assign opacity"),
5699                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5700                                                       NULL,
5701                                                       Inkscape::ICON_SIZE_DECORATION );
5702         g_object_set( act, "short_label", _("Assign"), NULL );
5703         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5704         g_object_set_data( holder, "set_action", act );
5705         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5706         // make sure it's disabled if we're not picking alpha
5707         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5708         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5709     }
5713 //########################
5714 //##      LPETool       ##
5715 //########################
5717 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5719 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5720 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5722     using namespace Inkscape::LivePathEffect;
5724     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5725     SPEventContext *ec = desktop->event_context;
5726     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5727         return;
5728     }
5730     // only take action if run by the attr_changed listener
5731     if (!g_object_get_data(tbl, "freeze")) {
5732         // in turn, prevent listener from responding
5733         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5735         gint mode = ege_select_one_action_get_active(act);
5736         EffectType type = lpesubtools[mode].type;
5738         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5739         bool success = lpetool_try_construction(lc, type);
5740         if (success) {
5741             // since the construction was already performed, we set the state back to inactive
5742             ege_select_one_action_set_active(act, 0);
5743             mode = 0;
5744         } else {
5745             // switch to the chosen subtool
5746             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5747         }
5749         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5750             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5751             prefs->setInt( "/tools/lpetool/mode", mode );
5752         }
5754         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5755     }
5758 static void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject * /*tbl*/)
5760     SPEventContext *ec = selection->desktop()->event_context;
5761     if (SP_IS_LPETOOL_CONTEXT(ec)) {
5762         lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5763     }
5766 static void sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5768     using namespace Inkscape::LivePathEffect;
5769     SPEventContext *ec = selection->desktop()->event_context;
5770     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5771         return;
5772     }
5773     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5775     lpetool_delete_measuring_items(lc);
5776     lpetool_create_measuring_items(lc, selection);
5778     // activate line segment combo box if a single item with LPELineSegment is selected
5779     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5780     SPItem *item = selection->singleItem();
5781     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5782         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5783         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5784         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5785             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5786             g_object_set_data(tbl, "currentlpe", lpe);
5787             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5788             gtk_action_set_sensitive(w, TRUE);
5789             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5790         } else {
5791             g_object_set_data(tbl, "currentlpe", NULL);
5792             g_object_set_data(tbl, "currentlpeitem", NULL);
5793             gtk_action_set_sensitive(w, FALSE);
5794         }
5795     } else {
5796         g_object_set_data(tbl, "currentlpe", NULL);
5797         g_object_set_data(tbl, "currentlpeitem", NULL);
5798         gtk_action_set_sensitive(w, FALSE);
5799     }
5802 static void lpetool_toggle_show_bbox(GtkToggleAction *act, gpointer data) {
5803     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5804     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5806     bool show = gtk_toggle_action_get_active( act );
5807     prefs->setBool("/tools/lpetool/show_bbox",  show);
5809     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5810         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5811         lpetool_context_reset_limiting_bbox(lc);
5812     }
5815 static void lpetool_toggle_show_measuring_info(GtkToggleAction *act, GObject *tbl)
5817     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5818     if (!tools_isactive(desktop, TOOLS_LPETOOL)) {
5819         return;
5820     }
5822     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5823     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5824     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5825         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5826         bool show = gtk_toggle_action_get_active( act );
5827         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5828         lpetool_show_measuring_info(lc, show);
5829         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5830     }
5833 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl)
5835     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5836     SPUnit const *unit = tracker->getActiveUnit();
5837     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5838     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5840     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5841     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5842         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5843         lpetool_delete_measuring_items(lc);
5844         lpetool_create_measuring_items(lc);
5845     }
5848 static void lpetool_toggle_set_bbox(GtkToggleAction *act, gpointer data)
5850     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5851     Inkscape::Selection *selection = desktop->selection;
5853     Geom::OptRect bbox = selection->bounds();
5855     if (bbox) {
5856         Geom::Point A(bbox->min());
5857         Geom::Point B(bbox->max());
5859         A *= desktop->doc2dt();
5860         B *= desktop->doc2dt();
5862         // TODO: should we provide a way to store points in prefs?
5863         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5864         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5865         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5866         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5867         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5869         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5870     }
5872     gtk_toggle_action_set_active(act, false);
5875 static void sp_line_segment_build_list(GObject *tbl)
5877     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5879     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5880     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5881     gtk_list_store_clear (model);
5883     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5884     {
5885         GtkTreeIter iter;
5886         gtk_list_store_append( model, &iter );
5887         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5888         gtk_list_store_append( model, &iter );
5889         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5890         gtk_list_store_append( model, &iter );
5891         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5892         gtk_list_store_append( model, &iter );
5893         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5894     }
5896     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5899 static void sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl)
5901     using namespace Inkscape::LivePathEffect;
5903     // quit if run by the attr_changed listener
5904     if (g_object_get_data(tbl, "freeze")) {
5905         return;
5906     }
5908     // in turn, prevent listener from responding
5909     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5911     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5912     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5913     if (lpeitem) {
5914         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5915         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5916         sp_lpe_item_update_patheffect(lpeitem, true, true);
5917     }
5919     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5922 static void lpetool_open_lpe_dialog(GtkToggleAction *act, gpointer data)
5924     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5926     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5927         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5928     }
5929     gtk_toggle_action_set_active(act, false);
5932 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5934     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5935     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5936     g_object_set_data(holder, "tracker", tracker);
5937     SPUnit const *unit = tracker->getActiveUnit();
5939     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5940     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5942     /** Automatically create a list of LPEs that get added to the toolbar **/
5943     {
5944         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5946         GtkTreeIter iter;
5948         // the first toggle button represents the state that no subtool is active (remove this when
5949         // this can be modeled by EgeSelectOneAction or some other action)
5950         gtk_list_store_append( model, &iter );
5951         gtk_list_store_set( model, &iter,
5952                             0, _("All inactive"),
5953                             1, _("No geometric tool is active"),
5954                             2, "draw-geometry-inactive",
5955                             -1 );
5957         Inkscape::LivePathEffect::EffectType type;
5958         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5959             type =  lpesubtools[i].type;
5960             gtk_list_store_append( model, &iter );
5961             gtk_list_store_set( model, &iter,
5962                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5963                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5964                                 2, lpesubtools[i].icon_name,
5965                                 -1 );
5966         }
5968         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5969         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5970         g_object_set_data( holder, "lpetool_mode_action", act );
5972         ege_select_one_action_set_appearance( act, "full" );
5973         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5974         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5975         ege_select_one_action_set_icon_column( act, 2 );
5976         ege_select_one_action_set_tooltip_column( act, 1  );
5978         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5979         ege_select_one_action_set_active( act, lpeToolMode );
5980         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5981     }
5983     /* Show limiting bounding box */
5984     {
5985         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5986                                                       _("Show limiting bounding box"),
5987                                                       _("Show bounding box (used to cut infinite lines)"),
5988                                                       "show-bounding-box",
5989                                                       Inkscape::ICON_SIZE_DECORATION );
5990         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5991         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5992         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5993     }
5995     /* Set limiting bounding box to bbox of current selection */
5996     {
5997         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5998                                                       _("Get limiting bounding box from selection"),
5999                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
6000                                                       "draw-geometry-set-bounding-box",
6001                                                       Inkscape::ICON_SIZE_DECORATION );
6002         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6003         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
6004         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6005     }
6008     /* Combo box to choose line segment type */
6009     {
6010         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
6011         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
6012         ege_select_one_action_set_appearance (act, "compact");
6013         g_object_set_data (holder, "lpetool_line_segment_action", act );
6015         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
6017         sp_line_segment_build_list (holder);
6019         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
6020         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
6021         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
6022     }
6024     /* Display measuring info for selected items */
6025     {
6026         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
6027                                                       _("Display measuring info"),
6028                                                       _("Display measuring info for selected items"),
6029                                                       "draw-geometry-show-measuring-info",
6030                                                       Inkscape::ICON_SIZE_DECORATION );
6031         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6032         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
6033         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
6034     }
6036     // add the units menu
6037     {
6038         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
6039         gtk_action_group_add_action( mainActions, act );
6040         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
6041         g_object_set_data(holder, "lpetool_units_action", act);
6042         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
6043     }
6045     /* Open LPE dialog (to adapt parameters numerically) */
6046     {
6047         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
6048                                                       _("Open LPE dialog"),
6049                                                       _("Open LPE dialog (to adapt parameters numerically)"),
6050                                                       "dialog-geometry",
6051                                                       Inkscape::ICON_SIZE_DECORATION );
6052         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
6053         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
6054         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6055     }
6057     //watch selection
6058     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
6060     sigc::connection *c_selection_modified =
6061         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6062                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
6063     pool->add_connection ("selection-modified", c_selection_modified);
6065     sigc::connection *c_selection_changed =
6066         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6067                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
6068     pool->add_connection ("selection-changed", c_selection_changed);
6071 //########################
6072 //##       Eraser       ##
6073 //########################
6075 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
6077     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6078     prefs->setDouble( "/tools/eraser/width", adj->value );
6079     update_presets_list(tbl);
6082 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6084     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6085     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
6086     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
6087         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6088         prefs->setBool( "/tools/eraser/mode", eraserMode );
6089     }
6091     // only take action if run by the attr_changed listener
6092     if (!g_object_get_data( tbl, "freeze" )) {
6093         // in turn, prevent listener from responding
6094         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6096         if ( eraserMode != 0 ) {
6097         } else {
6098         }
6099         // TODO finish implementation
6101         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6102     }
6105 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6107     {
6108         /* Width */
6109         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
6110         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
6111         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
6112                                                               _("Pen Width"), _("Width:"),
6113                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
6114                                                               "/tools/eraser/width", 15,
6115                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
6116                                                               1, 100, 1.0, 10.0,
6117                                                               labels, values, G_N_ELEMENTS(labels),
6118                                                               sp_erc_width_value_changed, 1, 0);
6119         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6120         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6121         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6122     }
6124     {
6125         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6127         GtkTreeIter iter;
6128         gtk_list_store_append( model, &iter );
6129         gtk_list_store_set( model, &iter,
6130                             0, _("Delete"),
6131                             1, _("Delete objects touched by the eraser"),
6132                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6133                             -1 );
6135         gtk_list_store_append( model, &iter );
6136         gtk_list_store_set( model, &iter,
6137                             0, _("Cut"),
6138                             1, _("Cut out from objects"),
6139                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6140                             -1 );
6142         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6143         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6144         g_object_set_data( holder, "eraser_mode_action", act );
6146         ege_select_one_action_set_appearance( act, "full" );
6147         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6148         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6149         ege_select_one_action_set_icon_column( act, 2 );
6150         ege_select_one_action_set_tooltip_column( act, 1  );
6152         /// @todo Convert to boolean?
6153         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6154         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6155         ege_select_one_action_set_active( act, eraserMode );
6156         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6157     }
6161 //########################
6162 //##    Text Toolbox    ##
6163 //########################
6164 /*
6165 static void sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6167     //Call back for letter sizing spinbutton
6170 static void sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6172     //Call back for line height spinbutton
6175 static void sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6177     //Call back for horizontal kerning spinbutton
6180 static void sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6182     //Call back for vertical kerning spinbutton
6185 static void sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6187     //Call back for letter rotation spinbutton
6188 }*/
6190 namespace {
6192 static void sp_text_toolbox_selection_changed(Inkscape::Selection * /*selection*/, GObject *tbl)
6194     // quit if run by the _changed callbacks
6195     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6196         return;
6197     }
6199     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6201     SPStyle *query =
6202         sp_style_new (SP_ACTIVE_DOCUMENT);
6204     int result_family =
6205         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6207     int result_style =
6208         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6210     int result_numbers =
6211         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6213     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6215     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6216     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6217         // there are no texts in selection, read from prefs
6219         sp_style_read_from_prefs(query, "/tools/text");
6221         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6222             // do not reset the toolbar style from prefs if we already did it last time
6223             sp_style_unref(query);
6224             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6225             return;
6226         }
6227         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6228     } else {
6229         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6230     }
6232     if (query->text)
6233     {
6234         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6235             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6236             gtk_entry_set_text (GTK_ENTRY (entry), "");
6238         } else if (query->text->font_specification.value || query->text->font_family.value) {
6240             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6241             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6243             // Get the font that corresponds
6244             Glib::ustring familyName;
6246             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6247             if (font) {
6248                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6249                 font->Unref();
6250                 font = NULL;
6251             }
6253             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6255             Gtk::TreeIter iter;
6256             try {
6257                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6258                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6259                 iter = model->get_iter(path);
6260             } catch (...) {
6261                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6262                 sp_style_unref(query);
6263                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6264                 return;
6265             }
6267             combo->set_active (iter);
6268         }
6270         //Size
6271         {
6272             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6273             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6274             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6275             g_free(str);
6276         }
6278         //Anchor
6279         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6280         {
6281             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6282             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6283             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6284             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6285         } else {
6286             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6287             {
6288                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6289                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6290                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6291                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6292             } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
6293                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6294                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6295                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6296                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6297             } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END) {
6298                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6299                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6300                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6301                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6302             }
6303         }
6305         //Style
6306         {
6307             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6309             gboolean active = gtk_toggle_button_get_active (button);
6310             gboolean check  = ((query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_NORMAL) && (query->font_weight.computed != SP_CSS_FONT_WEIGHT_LIGHTER));
6312             if (active != check)
6313             {
6314                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6315                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6316                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6317             }
6318         }
6320         {
6321             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6323             gboolean active = gtk_toggle_button_get_active (button);
6324             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6326             if (active != check)
6327             {
6328                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6329                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6330                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6331             }
6332         }
6334         //Orientation
6335         //locking both buttons, changing one affect all group (both)
6336         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6337         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6339         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6340         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6342         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
6343             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6344         } else {
6345             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6346         }
6347         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6348         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6349     }
6351     sp_style_unref(query);
6353     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6356 static void sp_text_toolbox_selection_modified(Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6358     sp_text_toolbox_selection_changed (selection, tbl);
6361 static void sp_text_toolbox_subselection_changed(gpointer /*tc*/, GObject *tbl)
6363     sp_text_toolbox_selection_changed (NULL, tbl);
6366 static void sp_text_toolbox_family_changed(GtkComboBoxEntry    *,
6367                                            GObject             *tbl)
6369     // quit if run by the _changed callbacks
6370     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6371         return;
6372     }
6374     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6376     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6377     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6378     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6380     //g_print ("family changed to: %s\n", family);
6382     SPStyle *query =
6383         sp_style_new (SP_ACTIVE_DOCUMENT);
6385     int result_fontspec =
6386         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6388     SPCSSAttr *css = sp_repr_css_attr_new ();
6390     // First try to get the font spec from the stored value
6391     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6393     if (fontSpec.empty()) {
6394         // Construct a new font specification if it does not yet exist
6395         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6396         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6397         fontFromStyle->Unref();
6398     }
6400     if (!fontSpec.empty()) {
6402         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6404         if (!newFontSpec.empty()) {
6406             if (fontSpec != newFontSpec) {
6408                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6410                 if (font) {
6411                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6413                     // Set all the these just in case they were altered when finding the best
6414                     // match for the new family and old style...
6416                     gchar c[256];
6418                     font->Family(c, 256);
6420                     sp_repr_css_set_property (css, "font-family", c);
6422                     font->Attribute( "weight", c, 256);
6423                     sp_repr_css_set_property (css, "font-weight", c);
6425                     font->Attribute("style", c, 256);
6426                     sp_repr_css_set_property (css, "font-style", c);
6428                     font->Attribute("stretch", c, 256);
6429                     sp_repr_css_set_property (css, "font-stretch", c);
6431                     font->Attribute("variant", c, 256);
6432                     sp_repr_css_set_property (css, "font-variant", c);
6434                     font->Unref();
6435                 }
6436             }
6438         } else {
6439             // If the old font on selection (or default) was not existing on the system,
6440             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6441             // setting the family reported by the family chooser.
6443             //g_print ("fallback setting family: %s\n", family);
6444             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6445             sp_repr_css_set_property (css, "font-family", family);
6446         }
6447     }
6449     // If querying returned nothing, set the default style of the tool (for new texts)
6450     if (result_fontspec == QUERY_STYLE_NOTHING) {
6451         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6452         prefs->mergeStyle("/tools/text/style", css);
6453         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6454     } else {
6455         sp_desktop_set_style (desktop, css, true, true);
6456     }
6458     sp_style_unref(query);
6460     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6461                                    _("Text: Change font family"));
6462     sp_repr_css_attr_unref (css);
6464     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6466     // unfreeze
6467     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6469     // focus to canvas
6470     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6474 static void sp_text_toolbox_anchoring_toggled(GtkRadioButton   *button,
6475                                               gpointer          data)
6477     if (g_object_get_data (G_OBJECT (button), "block")) {
6478         return;
6479     }
6480     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
6481         return;
6482     }
6483     int prop = GPOINTER_TO_INT(data);
6485     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6487     // move the x of all texts to preserve the same bbox
6488     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6489     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6490         if (SP_IS_TEXT((SPItem *) items->data)) {
6491             SPItem *item = SP_ITEM(items->data);
6493             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6494             // below, variable names suggest horizontal move, but we check the writing direction
6495             // and move in the corresponding axis
6496             int axis;
6497             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6498                 axis = NR::X;
6499             } else {
6500                 axis = NR::Y;
6501             }
6503             Geom::OptRect bbox
6504                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6505             if (!bbox) {
6506                 continue;
6507             }
6508             double width = bbox->dimensions()[axis];
6509             // If you want to align within some frame, other than the text's own bbox, calculate
6510             // the left and right (or top and bottom for tb text) slacks of the text inside that
6511             // frame (currently unused)
6512             double left_slack = 0;
6513             double right_slack = 0;
6514             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6515             double move = 0;
6516             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6517                 switch (prop) {
6518                     case 0:
6519                         move = -left_slack;
6520                         break;
6521                     case 1:
6522                         move = width/2 + (right_slack - left_slack)/2;
6523                         break;
6524                     case 2:
6525                         move = width + right_slack;
6526                         break;
6527                 }
6528             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6529                 switch (prop) {
6530                     case 0:
6531                         move = -width/2 - left_slack;
6532                         break;
6533                     case 1:
6534                         move = (right_slack - left_slack)/2;
6535                         break;
6536                     case 2:
6537                         move = width/2 + right_slack;
6538                         break;
6539                 }
6540             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6541                 switch (prop) {
6542                     case 0:
6543                         move = -width - left_slack;
6544                         break;
6545                     case 1:
6546                         move = -width/2 + (right_slack - left_slack)/2;
6547                         break;
6548                     case 2:
6549                         move = right_slack;
6550                         break;
6551                 }
6552             }
6553             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6554             if (axis == NR::X) {
6555                 XY = XY + Geom::Point (move, 0);
6556             } else {
6557                 XY = XY + Geom::Point (0, move);
6558             }
6559             SP_TEXT(item)->attributes.setFirstXY(XY);
6560             SP_OBJECT(item)->updateRepr();
6561             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6562         }
6563     }
6565     SPCSSAttr *css = sp_repr_css_attr_new ();
6566     switch (prop)
6567     {
6568         case 0:
6569         {
6570             sp_repr_css_set_property (css, "text-anchor", "start");
6571             sp_repr_css_set_property (css, "text-align", "start");
6572             break;
6573         }
6574         case 1:
6575         {
6576             sp_repr_css_set_property (css, "text-anchor", "middle");
6577             sp_repr_css_set_property (css, "text-align", "center");
6578             break;
6579         }
6581         case 2:
6582         {
6583             sp_repr_css_set_property (css, "text-anchor", "end");
6584             sp_repr_css_set_property (css, "text-align", "end");
6585             break;
6586         }
6588         case 3:
6589         {
6590             sp_repr_css_set_property (css, "text-anchor", "start");
6591             sp_repr_css_set_property (css, "text-align", "justify");
6592             break;
6593         }
6594     }
6596     SPStyle *query =
6597         sp_style_new (SP_ACTIVE_DOCUMENT);
6598     int result_numbers =
6599         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6601     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6602     if (result_numbers == QUERY_STYLE_NOTHING)
6603     {
6604         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6605         prefs->mergeStyle("/tools/text/style", css);
6606     }
6608     sp_style_unref(query);
6610     sp_desktop_set_style (desktop, css, true, true);
6611     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6612                                    _("Text: Change alignment"));
6613     sp_repr_css_attr_unref (css);
6615     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6618 static void sp_text_toolbox_style_toggled(GtkToggleButton  *button,
6619                                           gpointer          data)
6621     if (g_object_get_data (G_OBJECT (button), "block")) {
6622         return;
6623     }
6625     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6626     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6627     int          prop       = GPOINTER_TO_INT(data);
6628     bool         active     = gtk_toggle_button_get_active (button);
6630     SPStyle *query =
6631         sp_style_new (SP_ACTIVE_DOCUMENT);
6633     int result_fontspec =
6634         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6636     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6637     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6638     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6640     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6641     Glib::ustring newFontSpec = "";
6643     if (fontSpec.empty()) {
6644         // Construct a new font specification if it does not yet exist
6645         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6646         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6647         fontFromStyle->Unref();
6648     }
6650     bool nochange = true;
6651     switch (prop)
6652     {
6653         case 0:
6654         {
6655             if (!fontSpec.empty()) {
6656                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6657                 if (!newFontSpec.empty()) {
6658                     // Don't even set the bold if the font didn't exist on the system
6659                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6660                     nochange = false;
6661                 }
6662             }
6663             // set or reset the button according
6664             if(nochange) {
6665                 gboolean check = gtk_toggle_button_get_active (button);
6667                 if (active != check)
6668                 {
6669                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6670                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6671                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6672                 }
6673             }
6675             break;
6676         }
6678         case 1:
6679         {
6680             if (!fontSpec.empty()) {
6681                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6682                 if (!newFontSpec.empty()) {
6683                     // Don't even set the italic if the font didn't exist on the system
6684                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6685                     nochange = false;
6686                 }
6687             }
6688             if(nochange) {
6689                 gboolean check = gtk_toggle_button_get_active (button);
6691                 if (active != check)
6692                 {
6693                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6694                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6695                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6696                 }
6697             }
6698             break;
6699         }
6700     }
6702     if (!newFontSpec.empty()) {
6703         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6704     }
6706     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6707     if (result_fontspec == QUERY_STYLE_NOTHING)
6708     {
6709         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6710         prefs->mergeStyle("/tools/text/style", css);
6711     }
6713     sp_style_unref(query);
6715     sp_desktop_set_style (desktop, css, true, true);
6716     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6717                                    _("Text: Change font style"));
6718     sp_repr_css_attr_unref (css);
6720     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6723 static void sp_text_toolbox_orientation_toggled(GtkRadioButton  *button,
6724                                                 gpointer         data)
6726     if (g_object_get_data (G_OBJECT (button), "block")) {
6727         return;
6728     }
6730     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6731     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6732     int          prop       = GPOINTER_TO_INT(data);
6734     switch (prop)
6735     {
6736         case 0:
6737         {
6738             sp_repr_css_set_property (css, "writing-mode", "lr");
6739             break;
6740         }
6742         case 1:
6743         {
6744             sp_repr_css_set_property (css, "writing-mode", "tb");
6745             break;
6746         }
6747     }
6749     SPStyle *query =
6750         sp_style_new (SP_ACTIVE_DOCUMENT);
6751     int result_numbers =
6752         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6754     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6755     if (result_numbers == QUERY_STYLE_NOTHING)
6756     {
6757         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6758         prefs->mergeStyle("/tools/text/style", css);
6759     }
6761     sp_desktop_set_style (desktop, css, true, true);
6762     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6763                                    _("Text: Change orientation"));
6764     sp_repr_css_attr_unref (css);
6766     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6769 static gboolean sp_text_toolbox_family_keypress(GtkWidget * /*w*/, GdkEventKey *event, GObject *tbl)
6771     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6772     if (!desktop) {
6773         return FALSE;
6774     }
6776     switch (get_group0_keyval (event)) {
6777         case GDK_KP_Enter: // chosen
6778         case GDK_Return:
6779             // unfreeze and update, which will defocus
6780             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6781             sp_text_toolbox_family_changed (NULL, tbl);
6782             return TRUE; // I consumed the event
6783             break;
6784         case GDK_Escape:
6785             // defocus
6786             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6787             return TRUE; // I consumed the event
6788             break;
6789     }
6790     return FALSE;
6793 static gboolean sp_text_toolbox_family_list_keypress(GtkWidget * /*w*/, GdkEventKey *event, GObject * /*tbl*/)
6795     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6796     if (!desktop) {
6797         return FALSE;
6798     }
6800     switch (get_group0_keyval (event)) {
6801         case GDK_KP_Enter:
6802         case GDK_Return:
6803         case GDK_Escape: // defocus
6804             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6805             return TRUE; // I consumed the event
6806             break;
6807         case GDK_w:
6808         case GDK_W:
6809             if (event->state & GDK_CONTROL_MASK) {
6810                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6811                 return TRUE; // I consumed the event
6812             }
6813             break;
6814     }
6815     return FALSE;
6819 static void sp_text_toolbox_size_changed(GtkComboBox *cbox,
6820                                          GObject     *tbl)
6822      // quit if run by the _changed callbacks
6823     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6824         return;
6825     }
6827     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6829    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6831     // If this is not from selecting a size in the list (in which case get_active will give the
6832     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6833     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6834     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6835    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6836         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6837         return;
6838    }
6840     gdouble value = -1;
6841     {
6842         gchar *endptr;
6843         gchar *const text = gtk_combo_box_get_active_text(cbox);
6844         if (text) {
6845             value = g_strtod(text, &endptr);
6846             if (endptr == text) {  // Conversion failed, non-numeric input.
6847                 value = -1;
6848             }
6849             g_free(text);
6850         }
6851     }
6852     if (value <= 0) {
6853         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6854         return; // could not parse value
6855     }
6857     SPCSSAttr *css = sp_repr_css_attr_new ();
6858     Inkscape::CSSOStringStream osfs;
6859     osfs << value;
6860     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6862     SPStyle *query =
6863         sp_style_new (SP_ACTIVE_DOCUMENT);
6864     int result_numbers =
6865         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6867     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6868     if (result_numbers == QUERY_STYLE_NOTHING)
6869     {
6870         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6871         prefs->mergeStyle("/tools/text/style", css);
6872     }
6874     sp_style_unref(query);
6876     sp_desktop_set_style (desktop, css, true, true);
6877     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6878                                    _("Text: Change font size"));
6879     sp_repr_css_attr_unref (css);
6881     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6883     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6886 static gboolean sp_text_toolbox_size_focusout(GtkWidget * /*w*/, GdkEventFocus * /*event*/, GObject *tbl)
6888     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6889     if (!desktop) {
6890         return FALSE;
6891     }
6893     if (!g_object_get_data (tbl, "esc-pressed")) {
6894         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6895         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6896         sp_text_toolbox_size_changed (cbox, tbl);
6897         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6898     }
6899     return FALSE; // I consumed the event
6903 static gboolean sp_text_toolbox_size_keypress(GtkWidget * /*w*/, GdkEventKey *event, GObject *tbl)
6905     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6906     if (!desktop) {
6907         return FALSE;
6908     }
6910     switch (get_group0_keyval (event)) {
6911         case GDK_Escape: // defocus
6912             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6913             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6914             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6915             return TRUE; // I consumed the event
6916             break;
6917         case GDK_Return: // defocus
6918         case GDK_KP_Enter:
6919             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6920             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6921             sp_text_toolbox_size_changed (cbox, tbl);
6922             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6923             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6924             return TRUE; // I consumed the event
6925             break;
6926     }
6927     return FALSE;
6930 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6931 // does not work!
6932 static gboolean sp_text_toolbox_entry_focus_in(GtkWidget        *entry,
6933                                                GdkEventFocus    * /*event*/,
6934                                                GObject          *tbl)
6936     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6937     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6938     return FALSE;
6941 static gboolean sp_text_toolbox_entry_focus_out(GtkWidget        *entry,
6942                                                 GdkEventFocus    * /*event*/,
6943                                                 GObject          *tbl)
6945     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6946     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6947     return FALSE;
6950 static void cell_data_func(GtkCellLayout * /*cell_layout*/,
6951                            GtkCellRenderer   *cell,
6952                            GtkTreeModel      *tree_model,
6953                            GtkTreeIter       *iter,
6954                            gpointer           /*data*/)
6956     gchar *family;
6957     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6958     gchar *const family_escaped = g_markup_escape_text(family, -1);
6960     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6961     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6962     if (show_sample) {
6964         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6965         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6967     std::stringstream markup;
6968     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6969            << family_escaped << "'>" << sample_escaped << "</span>";
6970     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6972         g_free(sample_escaped);
6973     } else {
6974         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6975     }
6977     g_free(family);
6978     g_free(family_escaped);
6981 static gboolean text_toolbox_completion_match_selected(GtkEntryCompletion * /*widget*/,
6982                                                        GtkTreeModel       *model,
6983                                                        GtkTreeIter        *iter,
6984                                                        GObject            *tbl)
6986     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6987     // press Enter again after choosing a completion)
6988     gchar *family = 0;
6989     gtk_tree_model_get(model, iter, 0, &family, -1);
6991     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6992     gtk_entry_set_text (GTK_ENTRY (entry), family);
6994     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6995     sp_text_toolbox_family_changed (NULL, tbl);
6996     return TRUE;
7000 static void cbe_add_completion(GtkComboBoxEntry *cbe, GObject *tbl)
7002     GtkEntry *entry;
7003     GtkEntryCompletion *completion;
7004     GtkTreeModel *model;
7006     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
7007     completion = gtk_entry_completion_new();
7008     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
7009     gtk_entry_completion_set_model(completion, model);
7010     gtk_entry_completion_set_text_column(completion, 0);
7011     gtk_entry_completion_set_inline_completion(completion, FALSE);
7012     gtk_entry_completion_set_inline_selection(completion, FALSE);
7013     gtk_entry_completion_set_popup_completion(completion, TRUE);
7014     gtk_entry_set_completion(entry, completion);
7016     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
7018     g_object_unref(completion);
7021 static void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
7022                                              void * /*property*/,
7023                                              GObject *tbl)
7025   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
7027   gboolean shown;
7028   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
7029   if (shown) {
7030          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7031          //g_print("POP: notify: SHOWN\n");
7032   } else {
7033          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7035          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
7036          // find out if the drop down was closed by Enter and if so, manually update (only
7037          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
7038          GdkEvent *ev = gtk_get_current_event();
7039          if (ev) {
7040              //g_print ("ev type: %d\n", ev->type);
7041              if (ev->type == GDK_KEY_PRESS) {
7042                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
7043                      case GDK_KP_Enter: // chosen
7044                      case GDK_Return:
7045                      {
7046                          // make sure the chosen one is inserted into the entry
7047                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
7048                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
7049                          GtkTreeIter iter;
7050                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
7051                          if (has_active) {
7052                              gchar *family;
7053                              gtk_tree_model_get(model, &iter, 0, &family, -1);
7054                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
7055                              gtk_entry_set_text (GTK_ENTRY (entry), family);
7056                          }
7058                          // update
7059                          sp_text_toolbox_family_changed (NULL, tbl);
7060                          break;
7061                      }
7062                  }
7063              }
7064          }
7066          // regardless of whether we updated, defocus the widget
7067          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
7068          if (desktop) {
7069              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
7070          }
7071          //g_print("POP: notify: HIDDEN\n");
7072   }
7075 GtkWidget *sp_text_toolbox_new(SPDesktop *desktop)
7077     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
7078     GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
7080     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
7081     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
7083     GtkTooltips *tt = gtk_tooltips_new();
7085     ////////////Family
7086     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
7087     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
7089     gtk_rc_parse_string (
7090        "style \"dropdown-as-list-style\"\n"
7091        "{\n"
7092        "    GtkComboBox::appears-as-list = 1\n"
7093        "}\n"
7094        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
7095     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
7096     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
7098     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
7100     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
7102     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
7103     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
7105     // expand the field a bit so as to view more of the previews in the drop-down
7106     GtkRequisition req;
7107     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
7108     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
7110     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
7111     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7113     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7114     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
7115              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7116     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7117     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7118     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7120     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7121     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7123     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7124     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7125     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7126     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7128     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7129     GtkWidget *box = gtk_event_box_new ();
7130     gtk_container_add (GTK_CONTAINER (box), image);
7131     gtk_toolbar_append_widget( tbl, box, "", "");
7132     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7133     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7134     gtk_widget_hide (GTK_WIDGET (box));
7135     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7137     ////////////Size
7138     gchar const *const sizes[] = {
7139         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7140         "16", "18", "20", "22", "24", "28",
7141         "32", "36", "40", "48", "56", "64", "72", "144"
7142     };
7144     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7145     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7146         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7147     }
7148     gtk_widget_set_size_request (cbox, 80, -1);
7149     gtk_toolbar_append_widget( tbl, cbox, "", "");
7150     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7151     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7152     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7153     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7155     ////////////Text anchor
7156     GtkWidget *group   = gtk_radio_button_new (NULL);
7157     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7158     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7160     // left
7161     GtkWidget *rbutton = group;
7162     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7163     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7164     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7166     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7167     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7168     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7169     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7171     // center
7172     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7173     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7174     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7175     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7177     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7178     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7179     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7180     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7182     // right
7183     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7184     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7185     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7186     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7188     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7189     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7190     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7191     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7193     // fill
7194     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7195     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7196     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7197     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7199     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7200     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7201     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7202     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7204     gtk_toolbar_append_widget( tbl, row, "", "");
7206     //spacer
7207     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7209     ////////////Text style
7210     row = gtk_hbox_new (FALSE, 4);
7212     // bold
7213     rbutton = gtk_toggle_button_new ();
7214     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7215     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7216     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7217     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7219     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7220     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7221     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7223     // italic
7224     rbutton = gtk_toggle_button_new ();
7225     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7226     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7227     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7228     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7230     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7231     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7232     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7234     gtk_toolbar_append_widget( tbl, row, "", "");
7236     //spacer
7237     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7239     // Text orientation
7240     group   = gtk_radio_button_new (NULL);
7241     row     = gtk_hbox_new (FALSE, 4);
7242     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7244     // horizontal
7245     rbutton = group;
7246     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7247     gtk_container_add           (GTK_CONTAINER (rbutton),
7248                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7249     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7250     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7252     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7253     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7254     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7256     // vertical
7257     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7258     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7259     gtk_container_add           (GTK_CONTAINER (rbutton),
7260                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7261     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7262     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7264     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7265     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7266     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7267     gtk_toolbar_append_widget( tbl, row, "", "" );
7270     //watch selection
7271     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7273     sigc::connection *c_selection_changed =
7274         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7275                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7276     pool->add_connection ("selection-changed", c_selection_changed);
7278     sigc::connection *c_selection_modified =
7279         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7280                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7281     pool->add_connection ("selection-modified", c_selection_modified);
7283     sigc::connection *c_subselection_changed =
7284         new sigc::connection (desktop->connectToolSubselectionChanged
7285                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7286     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7288     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7291     gtk_widget_show_all( GTK_WIDGET(tbl) );
7293     return GTK_WIDGET(tbl);
7294 } // end of sp_text_toolbox_new()
7296 }//<unnamed> namespace
7299 //#########################
7300 //##      Connector      ##
7301 //#########################
7303 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7305     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7306     prefs->setBool("/tools/connector/mode",
7307                 gtk_toggle_action_get_active( act ));
7310 static void sp_connector_path_set_avoid(void)
7312     cc_selection_set_avoid(true);
7316 static void sp_connector_path_set_ignore(void)
7318     cc_selection_set_avoid(false);
7321 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7323     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7324     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7325     SPDocument *doc = sp_desktop_document(desktop);
7327     if (!sp_document_get_undo_sensitive(doc)) {
7328         return;
7329     }
7332     // quit if run by the _changed callbacks
7333     if (g_object_get_data( tbl, "freeze" )) {
7334         return;
7335     }
7337     // in turn, prevent callbacks from responding
7338     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7340     bool is_orthog = gtk_toggle_action_get_active( act );
7341     gchar orthog_str[] = "orthogonal";
7342     gchar polyline_str[] = "polyline";
7343     gchar *value = is_orthog ? orthog_str : polyline_str ;
7345     bool modmade = false;
7346     GSList *l = (GSList *) selection->itemList();
7347     while (l) {
7348         SPItem *item = (SPItem *) l->data;
7350         if (cc_item_is_connector(item)) {
7351             sp_object_setAttribute(item, "inkscape:connector-type",
7352                     value, false);
7353             item->avoidRef->handleSettingChange();
7354             modmade = true;
7355         }
7356         l = l->next;
7357     }
7359     if (!modmade) {
7360         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7361         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7362     }
7364     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7365             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7367     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7370 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7372     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7373     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7374     SPDocument *doc = sp_desktop_document(desktop);
7376     if (!sp_document_get_undo_sensitive(doc)) {
7377         return;
7378     }
7381     // quit if run by the _changed callbacks
7382     if (g_object_get_data( tbl, "freeze" )) {
7383         return;
7384     }
7386     // in turn, prevent callbacks from responding
7387     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7389     gdouble newValue = gtk_adjustment_get_value(adj);
7390     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7391     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7393     bool modmade = false;
7394     GSList *l = (GSList *) selection->itemList();
7395     while (l) {
7396         SPItem *item = (SPItem *) l->data;
7398         if (cc_item_is_connector(item)) {
7399             sp_object_setAttribute(item, "inkscape:connector-curvature",
7400                     value, false);
7401             item->avoidRef->handleSettingChange();
7402             modmade = true;
7403         }
7404         l = l->next;
7405     }
7407     if (!modmade) {
7408         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7409         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7410     }
7412     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7413             _("Change connector curvature"));
7415     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7419 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7421     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7422     SPDocument *doc = sp_desktop_document(desktop);
7424     if (!sp_document_get_undo_sensitive(doc)) {
7425         return;
7426     }
7428     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7430     if ( !repr->attribute("inkscape:connector-spacing") &&
7431             ( adj->value == defaultConnSpacing )) {
7432         // Don't need to update the repr if the attribute doesn't
7433         // exist and it is being set to the default value -- as will
7434         // happen at startup.
7435         return;
7436     }
7438     // quit if run by the attr_changed listener
7439     if (g_object_get_data( tbl, "freeze" )) {
7440         return;
7441     }
7443     // in turn, prevent listener from responding
7444     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7446     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7447     SP_OBJECT(desktop->namedview)->updateRepr();
7449     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7450     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7451         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7452         Geom::Matrix m = Geom::identity();
7453         avoid_item_move(&m, item);
7454     }
7456     if (items) {
7457         g_slist_free(items);
7458     }
7460     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7461             _("Change connector spacing"));
7463     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7466 static void sp_connector_graph_layout(void)
7468     if (!SP_ACTIVE_DESKTOP) {
7469         return;
7470     }
7471     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7473     // hack for clones, see comment in align-and-distribute.cpp
7474     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7475     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7477     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7479     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7481     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7484 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7486     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7487     prefs->setBool("/tools/connector/directedlayout",
7488                 gtk_toggle_action_get_active( act ));
7491 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject * /*tbl*/ )
7493     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7494     prefs->setBool("/tools/connector/avoidoverlaplayout",
7495                 gtk_toggle_action_get_active( act ));
7499 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7501     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7502     prefs->setDouble("/tools/connector/length", adj->value);
7505 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7506                                             gchar const *name, gchar const * /*old_value*/, gchar const * /*new_value*/,
7507                                             bool /*is_interactive*/, gpointer data)
7509     GtkWidget *tbl = GTK_WIDGET(data);
7511     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7512         return;
7513     }
7514     if (strcmp(name, "inkscape:connector-spacing") == 0)
7515     {
7516         GtkAdjustment *adj = (GtkAdjustment*)
7517                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7518         gdouble spacing = defaultConnSpacing;
7519         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7521         gtk_adjustment_set_value(adj, spacing);
7522         gtk_adjustment_value_changed(adj);
7523     }
7525     spinbutton_defocus(GTK_OBJECT(tbl));
7528 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7530     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7531     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7533     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
7534         cc_create_connection_point(cc);
7535     }
7538 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7540     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7541     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7543     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE) {
7544         cc_remove_connection_point(cc);
7545     }
7548 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7549     NULL, /* child_added */
7550     NULL, /* child_removed */
7551     connector_tb_event_attr_changed,
7552     NULL, /* content_changed */
7553     NULL  /* order_changed */
7554 };
7556 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7558     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7559     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7560     SPItem *item = selection->singleItem();
7561     if (SP_IS_PATH(item))
7562     {
7563         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7564         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7565         gtk_toggle_action_set_active(act, is_orthog);
7566         gtk_adjustment_set_value(adj, curvature);
7567     }
7571 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7573     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7574     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7576     // Editing mode toggle button
7577     {
7578         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7579                                                       _("EditMode"),
7580                                                       _("Switch between connection point editing and connector drawing mode"),
7581                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7582                                                       Inkscape::ICON_SIZE_DECORATION );
7583         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7585         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7586         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7587         g_object_set_data( holder, "mode", act );
7588         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7589     }
7592     {
7593         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7594                                           _("Avoid"),
7595                                           _("Make connectors avoid selected objects"),
7596                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7597                                           secondarySize );
7598         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7599         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7600     }
7602     {
7603         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7604                                           _("Ignore"),
7605                                           _("Make connectors ignore selected objects"),
7606                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7607                                           secondarySize );
7608         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7609         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7610     }
7612     // Orthogonal connectors toggle button
7613     {
7614         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7615                                                       _("Orthogonal"),
7616                                                       _("Make connector orthogonal or polyline"),
7617                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7618                                                       Inkscape::ICON_SIZE_DECORATION );
7619         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7621         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7622         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7623         g_object_set_data( holder, "orthogonal", act );
7624         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7625     }
7627     EgeAdjustmentAction* eact = 0;
7628     // Curvature spinbox
7629     eact = create_adjustment_action( "ConnectorCurvatureAction",
7630                                     _("Connector Curvature"), _("Curvature:"),
7631                                     _("The amount of connectors curvature"),
7632                                     "/tools/connector/curvature", defaultConnCurvature,
7633                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7634                                     0, 100, 1.0, 10.0,
7635                                     0, 0, 0,
7636                                     connector_curvature_changed, 1, 0 );
7637     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7639     // Spacing spinbox
7640     eact = create_adjustment_action( "ConnectorSpacingAction",
7641                                     _("Connector Spacing"), _("Spacing:"),
7642                                     _("The amount of space left around objects by auto-routing connectors"),
7643                                     "/tools/connector/spacing", defaultConnSpacing,
7644                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7645                                     0, 100, 1.0, 10.0,
7646                                     0, 0, 0,
7647                                     connector_spacing_changed, 1, 0 );
7648     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7650     // Graph (connector network) layout
7651     {
7652         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7653                                           _("Graph"),
7654                                           _("Nicely arrange selected connector network"),
7655                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7656                                           secondarySize );
7657         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7658         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7659     }
7661     // Default connector length spinbox
7662     eact = create_adjustment_action( "ConnectorLengthAction",
7663                                      _("Connector Length"), _("Length:"),
7664                                      _("Ideal length for connectors when layout is applied"),
7665                                      "/tools/connector/length", 100,
7666                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7667                                      10, 1000, 10.0, 100.0,
7668                                      0, 0, 0,
7669                                      connector_length_changed, 1, 0 );
7670     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7673     // Directed edges toggle button
7674     {
7675         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7676                                                       _("Downwards"),
7677                                                       _("Make connectors with end-markers (arrows) point downwards"),
7678                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7679                                                       Inkscape::ICON_SIZE_DECORATION );
7680         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7682         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7683         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7685         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7686         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder));
7687     }
7689     // Avoid overlaps toggle button
7690     {
7691         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7692                                                       _("Remove overlaps"),
7693                                                       _("Do not allow overlapping shapes"),
7694                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7695                                                       Inkscape::ICON_SIZE_DECORATION );
7696         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7698         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7699         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7701         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7702     }
7705     // New connection point button
7706     {
7707         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7708                                           _("New connection point"),
7709                                           _("Add a new connection point to the currently selected item"),
7710                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7711                                           secondarySize );
7712         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7713         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7714     }
7716     // Remove selected connection point button
7718     {
7719         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7720                                           _("Remove connection point"),
7721                                           _("Remove the currently selected connection point"),
7722                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7723                                           secondarySize );
7724         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7725         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7726     }
7729     // Code to watch for changes to the connector-spacing attribute in
7730     // the XML.
7731     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7732     g_assert(repr != NULL);
7734     purge_repr_listener( holder, holder );
7736     if (repr) {
7737         g_object_set_data( holder, "repr", repr );
7738         Inkscape::GC::anchor(repr);
7739         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7740         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7741     }
7742 } // end of sp_connector_toolbox_prep()
7745 //#########################
7746 //##     Paintbucket     ##
7747 //#########################
7749 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7751     gint channels = ege_select_one_action_get_active( act );
7752     flood_channels_set_channels( channels );
7755 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject * /*tbl*/)
7757     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7758     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7761 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject * /*tbl*/)
7763     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7764     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7767 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7769     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7770     SPUnit const *unit = tracker->getActiveUnit();
7771     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7773     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7774     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7777 static void paintbucket_defaults(GtkWidget *, GObject *tbl)
7779     // FIXME: make defaults settable via Inkscape Options
7780     struct KeyValue {
7781         char const *key;
7782         double value;
7783     } const key_values[] = {
7784         {"threshold", 15},
7785         {"offset", 0.0}
7786     };
7788     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7789         KeyValue const &kv = key_values[i];
7790         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7791         if ( adj ) {
7792             gtk_adjustment_set_value(adj, kv.value);
7793         }
7794     }
7796     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7797     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7798     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7799     ege_select_one_action_set_active( autogap_action, 0 );
7802 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7804     EgeAdjustmentAction* eact = 0;
7805     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7807     {
7808         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7810         GList* items = 0;
7811         gint count = 0;
7812         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7813         {
7814             GtkTreeIter iter;
7815             gtk_list_store_append( model, &iter );
7816             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7817             count++;
7818         }
7819         g_list_free( items );
7820         items = 0;
7821         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7822         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7823         ege_select_one_action_set_appearance( act1, "compact" );
7824         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7825         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7826         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7827         g_object_set_data( holder, "channels_action", act1 );
7828     }
7830     // Spacing spinbox
7831     {
7832         eact = create_adjustment_action(
7833             "ThresholdAction",
7834             _("Fill Threshold"), _("Threshold:"),
7835             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7836             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7837             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7838             0, 0, 0,
7839             paintbucket_threshold_changed, 1, 0 );
7841         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7842         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7843     }
7845     // Create the units menu.
7846     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7847     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7848     if (!stored_unit.empty()) {
7849         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7850     }
7851     g_object_set_data( holder, "tracker", tracker );
7852     {
7853         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7854         gtk_action_group_add_action( mainActions, act );
7855     }
7857     // Offset spinbox
7858     {
7859         eact = create_adjustment_action(
7860             "OffsetAction",
7861             _("Grow/shrink by"), _("Grow/shrink by:"),
7862             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7863             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7864             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7865             0, 0, 0,
7866             paintbucket_offset_changed, 1, 2);
7867         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7869         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7870     }
7872     /* Auto Gap */
7873     {
7874         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7876         GList* items = 0;
7877         gint count = 0;
7878         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7879         {
7880             GtkTreeIter iter;
7881             gtk_list_store_append( model, &iter );
7882             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7883             count++;
7884         }
7885         g_list_free( items );
7886         items = 0;
7887         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7888         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7889         ege_select_one_action_set_appearance( act2, "compact" );
7890         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7891         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7892         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7893         g_object_set_data( holder, "autogap_action", act2 );
7894     }
7896     /* Reset */
7897     {
7898         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7899                                           _("Defaults"),
7900                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7901                                           GTK_STOCK_CLEAR );
7902         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7903         gtk_action_group_add_action( mainActions, act );
7904         gtk_action_set_sensitive( act, TRUE );
7905     }
7909 /*
7910   Local Variables:
7911   mode:c++
7912   c-file-style:"stroustrup"
7913   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7914   indent-tabs-mode:nil
7915   fill-column:99
7916   End:
7917 */
7918 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :