Code

f81702f4f9c04841276767e1dbee2cac4a3265e9
[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/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         "    <toolitem action='NodesShowTransformHandlesAction' />"
286         "    <separator />"
287         "    <toolitem action='NodeInsertAction' />"
288         "    <toolitem action='NodeDeleteAction' />"
289         "    <separator />"
290         "    <toolitem action='NodeJoinAction' />"
291         "    <toolitem action='NodeBreakAction' />"
292         "    <separator />"
293         "    <toolitem action='NodeJoinSegmentAction' />"
294         "    <toolitem action='NodeDeleteSegmentAction' />"
295         "    <separator />"
296         "    <toolitem action='NodeCuspAction' />"
297         "    <toolitem action='NodeSmoothAction' />"
298         "    <toolitem action='NodeSymmetricAction' />"
299         "    <toolitem action='NodeAutoAction' />"
300         "    <separator />"
301         "    <toolitem action='NodeLineAction' />"
302         "    <toolitem action='NodeCurveAction' />"
303         "    <separator />"
304         //"    <toolitem action='ObjectToPath' />"
305         //"    <toolitem action='StrokeToPath' />"
306         //"    <separator />"
307         "    <toolitem action='NodeXAction' />"
308         "    <toolitem action='NodeYAction' />"
309         "    <toolitem action='NodeUnitsAction' />"
310         "    <separator />"
311         "    <toolitem action='ObjectEditClipPathAction' />"
312         "    <toolitem action='ObjectEditMaskPathAction' />"
313         "    <toolitem action='EditNextLPEParameterAction' />"
314         "    <separator />"
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 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 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) {
669     connection->disconnect();
670     delete connection;
673 static void purge_repr_listener( GObject* obj, GObject* tbl )
675     (void)obj;
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 GtkWidget *
686 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
687                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
688                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
690     SPAction *action = verb->get_action(view);
691     if (!action) return NULL;
693     SPAction *doubleclick_action;
694     if (doubleclick_verb)
695         doubleclick_action = doubleclick_verb->get_action(view);
696     else
697         doubleclick_action = NULL;
699     /* fixme: Handle sensitive/unsensitive */
700     /* fixme: Implement sp_button_new_from_action */
701     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
702     gtk_widget_show(b);
705     unsigned int shortcut = sp_shortcut_get_primary(verb);
706     if (shortcut) {
707         gchar key[256];
708         sp_ui_shortcut_string(shortcut, key);
709         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
710         if ( t ) {
711             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
712         }
713         g_free(tip);
714     } else {
715         if ( t ) {
716             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
717         }
718     }
720     return b;
724 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
726     SPAction* targetAction = SP_ACTION(user_data);
727     if ( targetAction ) {
728         sp_action_perform( targetAction, NULL );
729     }
732 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
734     if ( data ) {
735         GtkAction* act = GTK_ACTION(data);
736         gtk_action_set_sensitive( act, sensitive );
737     }
740 static SPActionEventVector action_event_vector = {
741     {NULL},
742     NULL,
743     NULL,
744     sp_action_action_set_sensitive,
745     NULL,
746     NULL
747 };
749 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
751     GtkAction* act = 0;
753     SPAction* targetAction = verb->get_action(view);
754     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
755     act = GTK_ACTION(inky);
756     gtk_action_set_sensitive( act, targetAction->sensitive );
758     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
760     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
761     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
763     return act;
766 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
768     Inkscape::UI::View::View *view = desktop;
769     gint verbsToUse[] = {
770         // disabled until we have icons for them:
771         //find
772         //SP_VERB_EDIT_TILE,
773         //SP_VERB_EDIT_UNTILE,
774         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
775         SP_VERB_DIALOG_DISPLAY,
776         SP_VERB_DIALOG_FILL_STROKE,
777         SP_VERB_DIALOG_NAMEDVIEW,
778         SP_VERB_DIALOG_TEXT,
779         SP_VERB_DIALOG_XML_EDITOR,
780         SP_VERB_DIALOG_LAYERS,
781         SP_VERB_EDIT_CLONE,
782         SP_VERB_EDIT_COPY,
783         SP_VERB_EDIT_CUT,
784         SP_VERB_EDIT_DUPLICATE,
785         SP_VERB_EDIT_PASTE,
786         SP_VERB_EDIT_REDO,
787         SP_VERB_EDIT_UNDO,
788         SP_VERB_EDIT_UNLINK_CLONE,
789         SP_VERB_FILE_EXPORT,
790         SP_VERB_FILE_IMPORT,
791         SP_VERB_FILE_NEW,
792         SP_VERB_FILE_OPEN,
793         SP_VERB_FILE_PRINT,
794         SP_VERB_FILE_SAVE,
795         SP_VERB_OBJECT_TO_CURVE,
796         SP_VERB_SELECTION_GROUP,
797         SP_VERB_SELECTION_OUTLINE,
798         SP_VERB_SELECTION_UNGROUP,
799         SP_VERB_ZOOM_1_1,
800         SP_VERB_ZOOM_1_2,
801         SP_VERB_ZOOM_2_1,
802         SP_VERB_ZOOM_DRAWING,
803         SP_VERB_ZOOM_IN,
804         SP_VERB_ZOOM_NEXT,
805         SP_VERB_ZOOM_OUT,
806         SP_VERB_ZOOM_PAGE,
807         SP_VERB_ZOOM_PAGE_WIDTH,
808         SP_VERB_ZOOM_PREV,
809         SP_VERB_ZOOM_SELECTION,
810     };
812     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
814     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
815     Glib::RefPtr<Gtk::ActionGroup> mainActions;
816     if ( groups.find(desktop) != groups.end() ) {
817         mainActions = groups[desktop];
818     }
820     if ( !mainActions ) {
821         mainActions = Gtk::ActionGroup::create("main");
822         groups[desktop] = mainActions;
823     }
825     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
826         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
827         if ( verb ) {
828             if (!mainActions->get_action(verb->get_id())) {
829                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
830                 mainActions->add(Glib::wrap(act));
831             }
832         }
833     }
835     if ( !mainActions->get_action("ToolZoom") ) {
836         GtkTooltips *tt = gtk_tooltips_new();
837         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
838             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
839             if ( va ) {
840                 mainActions->add(va);
841                 if ( i == 0 ) {
842                     va->set_active(true);
843                 }
844             }
845         }
846     }
848 #if ENABLE_TASK_SUPPORT
849     if ( !mainActions->get_action("TaskSetAction") ) {
850         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_STRING );
852         GtkTreeIter iter;
853         gtk_list_store_append( model, &iter );
854         gtk_list_store_set( model, &iter,
855                             0, _("Default"),
856                             1, _("Default interface setup"),
857                             -1 );
859         gtk_list_store_append( model, &iter );
860         gtk_list_store_set( model, &iter,
861                             0, _("Custom"),
862                             1, _("Set the custom task"),
863                             -1 );
865         gtk_list_store_append( model, &iter );
866         gtk_list_store_set( model, &iter,
867                             0, _("Wide"),
868                             1, _("Setup for widescreen work"),
869                             -1 );
871         EgeSelectOneAction* act = ege_select_one_action_new( "TaskSetAction", _("Task"), (""), NULL, GTK_TREE_MODEL(model) );
872         g_object_set( act, "short_label", _("Task:"), NULL );
873         mainActions->add(Glib::wrap(GTK_ACTION(act)));
874         //g_object_set_data( holder, "mode_action", act );
876         ege_select_one_action_set_appearance( act, "minimal" );
877         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
878         //ege_select_one_action_set_icon_size( act, secondarySize );
879         ege_select_one_action_set_tooltip_column( act, 1  );
881         //ege_select_one_action_set_active( act, mode );
882         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(fireTaskChange), desktop );
883     }
884 #endif // ENABLE_TASK_SUPPORT
886     return mainActions;
890 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
892     gtk_widget_set_size_request( widget,
893                                  widget->allocation.width,
894                                  widget->allocation.height );
897 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
899     gtk_widget_set_size_request( widget, -1, -1 );
902 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
904     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
906     gtk_widget_set_sensitive(tb, FALSE);
908     GtkWidget *hb = 0;
909     if ( UXManager::getInstance()->isFloatWindowProblem() ) {
910         hb = gtk_event_box_new(); // A simple, neutral container.
911     } else {
912         hb = gtk_handle_box_new();
913         gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
914         gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
915         gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
916     }
918     gtk_container_add(GTK_CONTAINER(hb), tb);
919     gtk_widget_show(GTK_WIDGET(tb));
921     sigc::connection* conn = new sigc::connection;
922     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
924     if ( GTK_IS_HANDLE_BOX(hb) ) {
925         g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
926         g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
927     }
929     gpointer val = GINT_TO_POINTER(id);
930     g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
932     return hb;
935 GtkWidget *ToolboxFactory::createToolToolbox()
937     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
939     return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
942 GtkWidget *ToolboxFactory::createAuxToolbox()
944     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
946     return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
949 //####################################
950 //# Commands Bar
951 //####################################
953 GtkWidget *ToolboxFactory::createCommandsToolbox()
955     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
957     return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
960 GtkWidget *ToolboxFactory::createSnapToolbox()
962     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
964     return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
967 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
968                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
969                                                        Glib::ustring const &path, gdouble def,
970                                                        GtkWidget *focusTarget,
971                                                        GtkWidget *us,
972                                                        GObject *dataKludge,
973                                                        gboolean altx, gchar const *altx_mark,
974                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
975                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
976                                                        void (*callback)(GtkAdjustment *, GObject *),
977                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
979     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
980     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
981                                                              lower, upper, step, page, 0 ) );
982     if (us) {
983         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
984     }
986     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
988     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
989     if ( shortLabel ) {
990         g_object_set( act, "short_label", shortLabel, NULL );
991     }
993     if ( (descrCount > 0) && descrLabels && descrValues ) {
994         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
995     }
997     if ( focusTarget ) {
998         ege_adjustment_action_set_focuswidget( act, focusTarget );
999     }
1001     if ( altx && altx_mark ) {
1002         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
1003     }
1005     if ( dataKludge ) {
1006         // Rather lame, but it's the only place where we need to get the entry name
1007         // but we don't have an Entry
1008         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
1009     }
1011     // Using a cast just to make sure we pass in the right kind of function pointer
1012     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
1014     return act;
1018 //####################################
1019 //# node editing callbacks
1020 //####################################
1022 /** Temporary hack: Returns the node tool in the active desktop.
1023  * Will go away during tool refactoring. */
1024 static InkNodeTool *get_node_tool()
1026     if (!SP_ACTIVE_DESKTOP) return NULL;
1027     SPEventContext *ec = SP_ACTIVE_DESKTOP->event_context;
1028     if (!INK_IS_NODE_TOOL(ec)) return NULL;
1029     return static_cast<InkNodeTool*>(ec);
1032 void
1033 sp_node_path_edit_add(void)
1035     InkNodeTool *nt = get_node_tool();
1036     if (nt) {
1037         nt->_multipath->insertNodes();
1038     }
1041 void
1042 sp_node_path_edit_delete(void)
1044     InkNodeTool *nt = get_node_tool();
1045     if (nt) {
1046         nt->_multipath->deleteNodes();
1047     }
1050 void
1051 sp_node_path_edit_delete_segment(void)
1053     InkNodeTool *nt = get_node_tool();
1054     if (nt) {
1055         nt->_multipath->deleteSegments();
1056     }
1059 void
1060 sp_node_path_edit_break(void)
1062     InkNodeTool *nt = get_node_tool();
1063     if (nt) {
1064         nt->_multipath->breakNodes();
1065     }
1068 void
1069 sp_node_path_edit_join(void)
1071     InkNodeTool *nt = get_node_tool();
1072     if (nt) {
1073         nt->_multipath->joinNodes();
1074     }
1077 void
1078 sp_node_path_edit_join_segment(void)
1080     InkNodeTool *nt = get_node_tool();
1081     if (nt) {
1082         nt->_multipath->joinSegments();
1083     }
1086 void
1087 sp_node_path_edit_toline(void)
1089     InkNodeTool *nt = get_node_tool();
1090     if (nt) {
1091         nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT);
1092     }
1095 void
1096 sp_node_path_edit_tocurve(void)
1098     InkNodeTool *nt = get_node_tool();
1099     if (nt) {
1100         nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER);
1101     }
1104 void
1105 sp_node_path_edit_cusp(void)
1107     InkNodeTool *nt = get_node_tool();
1108     if (nt) {
1109         nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP);
1110     }
1113 void
1114 sp_node_path_edit_smooth(void)
1116     InkNodeTool *nt = get_node_tool();
1117     if (nt) {
1118         nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH);
1119     }
1122 void
1123 sp_node_path_edit_symmetrical(void)
1125     InkNodeTool *nt = get_node_tool();
1126     if (nt) {
1127         nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC);
1128     }
1131 void
1132 sp_node_path_edit_auto(void)
1134     InkNodeTool *nt = get_node_tool();
1135     if (nt) {
1136         nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO);
1137     }
1140 static void toggle_show_transform_handles (GtkToggleAction *act, gpointer /*data*/) {
1141     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1142     bool show = gtk_toggle_action_get_active( act );
1143     prefs->setBool("/tools/nodes/show_transform_handles", show);
1146 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1147     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1148     bool show = gtk_toggle_action_get_active( act );
1149     prefs->setBool("/tools/nodes/show_handles",  show);
1152 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1153     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1154     bool show = gtk_toggle_action_get_active( act );
1155     prefs->setBool("/tools/nodes/show_outline",  show);
1158 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1159     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1162 void toggle_edit_clip (GtkToggleAction *act, gpointer /*data*/) {
1163     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1164     bool edit = gtk_toggle_action_get_active( act );
1165     prefs->setBool("/tools/nodes/edit_clipping_paths", edit);
1168 void toggle_edit_mask (GtkToggleAction *act, gpointer /*data*/) {
1169     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1170     bool edit = gtk_toggle_action_get_active( act );
1171     prefs->setBool("/tools/nodes/edit_masks", edit);
1174 /* is called when the node selection is modified */
1175 static void
1176 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1178     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1179     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1180     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1181     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1183     // quit if run by the attr_changed listener
1184     if (g_object_get_data( tbl, "freeze" )) {
1185         return;
1186     }
1188     // in turn, prevent listener from responding
1189     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1191     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1192     SPUnit const *unit = tracker->getActiveUnit();
1194     InkNodeTool *nt = get_node_tool();
1195     if (!nt || nt->_selected_nodes->empty()) {
1196         // no path selected
1197         gtk_action_set_sensitive(xact, FALSE);
1198         gtk_action_set_sensitive(yact, FALSE);
1199     } else {
1200         gtk_action_set_sensitive(xact, TRUE);
1201         gtk_action_set_sensitive(yact, TRUE);
1202         Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1203         Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1204         Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint();
1206         if (oldx != mid[Geom::X])
1207             gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit));
1208         if (oldy != mid[Geom::Y])
1209             gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit));
1210     }
1212     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1215 static void
1216 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d)
1218     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1219     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1221     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1222     SPUnit const *unit = tracker->getActiveUnit();
1224     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1225         prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"),
1226             sp_units_get_pixels(adj->value, *unit));
1227     }
1229     // quit if run by the attr_changed listener
1230     if (g_object_get_data( tbl, "freeze" )) {
1231         return;
1232     }
1234     // in turn, prevent listener from responding
1235     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1237     InkNodeTool *nt = get_node_tool();
1238     if (nt && !nt->_selected_nodes->empty()) {
1239         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1240         double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d];
1241         Geom::Point delta(0,0);
1242         delta[d] = val - oldval;
1243         nt->_multipath->move(delta);
1244     }
1246     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1249 static void
1250 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1252     sp_node_path_value_changed(adj, tbl, Geom::X);
1255 static void
1256 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1258     sp_node_path_value_changed(adj, tbl, Geom::Y);
1261 void
1262 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1264     {
1265     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1266     SPItem *item = selection->singleItem();
1267     if (item && SP_IS_LPE_ITEM(item)) {
1268        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1269            gtk_action_set_sensitive(w, TRUE);
1270        } else {
1271            gtk_action_set_sensitive(w, FALSE);
1272        }
1273     } else {
1274        gtk_action_set_sensitive(w, FALSE);
1275     }
1276     }
1279 void
1280 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1282     sp_node_toolbox_sel_changed (selection, tbl);
1287 //################################
1288 //##    Node Editing Toolbox    ##
1289 //################################
1291 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1293     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1294     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1295     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1296     g_object_set_data( holder, "tracker", tracker );
1298     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1300     {
1301         InkAction* inky = ink_action_new( "NodeInsertAction",
1302                                           _("Insert node"),
1303                                           _("Insert new nodes into selected segments"),
1304                                           INKSCAPE_ICON_NODE_ADD,
1305                                           secondarySize );
1306         g_object_set( inky, "short_label", _("Insert"), NULL );
1307         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1308         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1309     }
1311     {
1312         InkAction* inky = ink_action_new( "NodeDeleteAction",
1313                                           _("Delete node"),
1314                                           _("Delete selected nodes"),
1315                                           INKSCAPE_ICON_NODE_DELETE,
1316                                           secondarySize );
1317         g_object_set( inky, "short_label", _("Delete"), NULL );
1318         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1319         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1320     }
1322     {
1323         InkAction* inky = ink_action_new( "NodeJoinAction",
1324                                           _("Join nodes"),
1325                                           _("Join selected nodes"),
1326                                           INKSCAPE_ICON_NODE_JOIN,
1327                                           secondarySize );
1328         g_object_set( inky, "short_label", _("Join"), NULL );
1329         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1330         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1331     }
1333     {
1334         InkAction* inky = ink_action_new( "NodeBreakAction",
1335                                           _("Break nodes"),
1336                                           _("Break path at selected nodes"),
1337                                           INKSCAPE_ICON_NODE_BREAK,
1338                                           secondarySize );
1339         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1340         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1341     }
1344     {
1345         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1346                                           _("Join with segment"),
1347                                           _("Join selected endnodes with a new segment"),
1348                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1349                                           secondarySize );
1350         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1351         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1352     }
1354     {
1355         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1356                                           _("Delete segment"),
1357                                           _("Delete segment between two non-endpoint nodes"),
1358                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1359                                           secondarySize );
1360         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1361         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1362     }
1364     {
1365         InkAction* inky = ink_action_new( "NodeCuspAction",
1366                                           _("Node Cusp"),
1367                                           _("Make selected nodes corner"),
1368                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1369                                           secondarySize );
1370         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1371         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1372     }
1374     {
1375         InkAction* inky = ink_action_new( "NodeSmoothAction",
1376                                           _("Node Smooth"),
1377                                           _("Make selected nodes smooth"),
1378                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1379                                           secondarySize );
1380         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1381         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1382     }
1384     {
1385         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1386                                           _("Node Symmetric"),
1387                                           _("Make selected nodes symmetric"),
1388                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1389                                           secondarySize );
1390         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1391         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1392     }
1394     {
1395         InkAction* inky = ink_action_new( "NodeAutoAction",
1396                                           _("Node Auto"),
1397                                           _("Make selected nodes auto-smooth"),
1398                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1399                                           secondarySize );
1400         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1401         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1402     }
1404     {
1405         InkAction* inky = ink_action_new( "NodeLineAction",
1406                                           _("Node Line"),
1407                                           _("Make selected segments lines"),
1408                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1409                                           secondarySize );
1410         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1411         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1412     }
1414     {
1415         InkAction* inky = ink_action_new( "NodeCurveAction",
1416                                           _("Node Curve"),
1417                                           _("Make selected segments curves"),
1418                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1419                                           secondarySize );
1420         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1421         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1422     }
1424     {
1425         InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction",
1426                                                       _("Show Transform Handles"),
1427                                                       _("Show node transformation handles"),
1428                                                       "node-transform",
1429                                                       secondarySize );
1430         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1431         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_transform_handles), desktop );
1432         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_transform_handles", false) );
1433     }
1435     {
1436         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1437                                                       _("Show Handles"),
1438                                                       _("Show the Bezier handles of selected nodes"),
1439                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1440                                                       secondarySize );
1441         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1442         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1443         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1444     }
1446     {
1447         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1448                                                       _("Show Outline"),
1449                                                       _("Show the outline of the path"),
1450                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1451                                                       secondarySize );
1452         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1453         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1454         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_outline", false) );
1455     }
1457     {
1458         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1459                                           _("Next path effect parameter"),
1460                                           _("Show next path effect parameter for editing"),
1461                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1462                                           secondarySize );
1463         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1464         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1465         g_object_set_data( holder, "nodes_lpeedit", inky);
1466     }
1468     {
1469         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction",
1470                                           _("Edit clipping paths"),
1471                                           _("Show editing controls for clipping paths of selected objects"),
1472                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1473                                           secondarySize );
1474         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1475         g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_clip), desktop );
1476         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_clipping_paths") );
1477     }
1479     {
1480         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction",
1481                                           _("Edit masks"),
1482                                           _("Show editing controls for masks of selected objects"),
1483                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1484                                           secondarySize );
1485         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1486         g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_mask), desktop );
1487         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_masks") );
1488     }
1490     /* X coord of selected node(s) */
1491     {
1492         EgeAdjustmentAction* eact = 0;
1493         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1494         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1495         eact = create_adjustment_action( "NodeXAction",
1496                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1497                                          "/tools/nodes/Xcoord", 0,
1498                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1499                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1500                                          labels, values, G_N_ELEMENTS(labels),
1501                                          sp_node_path_x_value_changed );
1502         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1503         g_object_set_data( holder, "nodes_x_action", eact );
1504         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1505         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1506     }
1508     /* Y coord of selected node(s) */
1509     {
1510         EgeAdjustmentAction* eact = 0;
1511         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1512         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1513         eact = create_adjustment_action( "NodeYAction",
1514                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1515                                          "/tools/nodes/Ycoord", 0,
1516                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1517                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1518                                          labels, values, G_N_ELEMENTS(labels),
1519                                          sp_node_path_y_value_changed );
1520         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1521         g_object_set_data( holder, "nodes_y_action", eact );
1522         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1523         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1524     }
1526     // add the units menu
1527     {
1528         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1529         gtk_action_group_add_action( mainActions, act );
1530     }
1533     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1535     //watch selection
1536     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1538     sigc::connection *c_selection_changed =
1539         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1540                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1541     pool->add_connection ("selection-changed", c_selection_changed);
1543     sigc::connection *c_selection_modified =
1544         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1545                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1546     pool->add_connection ("selection-modified", c_selection_modified);
1548     sigc::connection *c_subselection_changed =
1549         new sigc::connection (desktop->connectToolSubselectionChanged
1550                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1551     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1553     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1555     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1556 } // end of sp_node_toolbox_prep()
1559 //########################
1560 //##    Zoom Toolbox    ##
1561 //########################
1563 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1565     // no custom GtkAction setup needed
1566 } // end of sp_zoom_toolbox_prep()
1568 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1570     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1571                                                                               "event_context_connection"));
1573     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1575     SetupFunction setup_func = 0;
1576     UpdateFunction update_func = 0;
1578     switch (id) {
1579         case BAR_TOOL:
1580             setup_func = setup_tool_toolbox;
1581             update_func = update_tool_toolbox;
1582             break;
1584         case BAR_AUX:
1585             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1586             setup_func = setup_aux_toolbox;
1587             update_func = update_aux_toolbox;
1588             break;
1590         case BAR_COMMANDS:
1591             setup_func = setup_commands_toolbox;
1592             update_func = update_commands_toolbox;
1593             break;
1595         case BAR_SNAP:
1596             setup_func = setup_snap_toolbox;
1597             update_func = updateSnapToolbox;
1598             break;
1599         default:
1600             g_warning("Unexpected toolbox id encountered.");
1601     }
1603     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1604     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1606     if (old_desktop) {
1607         GList *children, *iter;
1609         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1610         for ( iter = children ; iter ; iter = iter->next ) {
1611             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1612         }
1613         g_list_free(children);
1614     }
1616     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1618     if (desktop && setup_func && update_func) {
1619         gtk_widget_set_sensitive(toolbox, TRUE);
1620         setup_func(toolbox, desktop);
1621         update_func(desktop, desktop->event_context, toolbox);
1622         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1623     } else {
1624         gtk_widget_set_sensitive(toolbox, FALSE);
1625     }
1627 } // end of sp_toolbox_set_desktop()
1630 static void setupToolboxCommon( GtkWidget *toolbox,
1631                                 SPDesktop *desktop,
1632                                 gchar const *descr,
1633                                 gchar const* toolbarName,
1634                                 gchar const* sizePref )
1636     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1637     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1639     GtkUIManager* mgr = gtk_ui_manager_new();
1640     GError* errVal = 0;
1642     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1644     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1645     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1647     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1648     if ( prefs->getBool("/toolbox/icononly", true) ) {
1649         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1650     }
1652     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1653     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1655     if (GTK_IS_HANDLE_BOX(toolbox)) {
1656         // g_message("GRABBING ORIENTATION   [%s]", toolbarName);
1657         GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1658         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1659     } else {
1660         GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK )));
1661         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1662     }
1663     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1664     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1666     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1668     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1669     if ( child ) {
1670         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1671     }
1673     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1676 #define noDUMP_DETAILS 1
1678 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1680 #if DUMP_DETAILS
1681     g_message("Set orientation for %p to be %d", toolbox, orientation);
1682     GType type = GTK_WIDGET_TYPE(toolbox);
1683     g_message("        [%s]", g_type_name(type));
1684     g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1685 #endif
1687     GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1688     GtkHandleBox* handleBox = 0;
1690     if (GTK_IS_BIN(toolbox)) {
1691 #if DUMP_DETAILS
1692         g_message("            is a BIN");
1693 #endif // DUMP_DETAILS
1694         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1695         if (child) {
1696 #if DUMP_DETAILS
1697             GType type2 = GTK_WIDGET_TYPE(child);
1698             g_message("            child    [%s]", g_type_name(type2));
1699 #endif // DUMP_DETAILS
1701             if (GTK_IS_BOX(child)) {
1702 #if DUMP_DETAILS
1703                 g_message("                is a BOX");
1704 #endif // DUMP_DETAILS
1706                 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1707                 if (children) {
1708                     for (GList* curr = children; curr; curr = g_list_next(curr)) {
1709                         GtkWidget* child2 = GTK_WIDGET(curr->data);
1710 #if DUMP_DETAILS
1711                         GType type3 = GTK_WIDGET_TYPE(child2);
1712                         g_message("                child2   [%s]", g_type_name(type3));
1713 #endif // DUMP_DETAILS
1715                         if (GTK_IS_CONTAINER(child2)) {
1716                             GList* children2 = gtk_container_get_children(GTK_CONTAINER(child2));
1717                             if (children2) {
1718                                 for (GList* curr2 = children2; curr2; curr2 = g_list_next(curr2)) {
1719                                     GtkWidget* child3 = GTK_WIDGET(curr2->data);
1720 #if DUMP_DETAILS
1721                                     GType type4 = GTK_WIDGET_TYPE(child3);
1722                                     g_message("                    child3   [%s]", g_type_name(type4));
1723 #endif // DUMP_DETAILS
1724                                     if (GTK_IS_TOOLBAR(child3)) {
1725                                         GtkToolbar* childBar = GTK_TOOLBAR(child3);
1726                                         gtk_toolbar_set_orientation(childBar, orientation);
1727                                     }
1728                                 }
1729                                 g_list_free(children2);
1730                             }
1731                         }
1734                         if (GTK_IS_TOOLBAR(child2)) {
1735                             GtkToolbar* childBar = GTK_TOOLBAR(child2);
1736                             gtk_toolbar_set_orientation(childBar, orientation);
1737                             if (GTK_IS_HANDLE_BOX(toolbox)) {
1738                                 handleBox = GTK_HANDLE_BOX(toolbox);
1739                             }
1740                         } else {
1741                             g_message("need to add dynamic switch");
1742                         }
1743                     }
1744                     g_list_free(children);
1745                 } else {
1746                     // The call is being made before the toolbox proper has been setup.
1747                     if (GTK_IS_HANDLE_BOX(toolbox)) {
1748                         handleBox = GTK_HANDLE_BOX(toolbox);
1749                     } else {
1750                         g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos));
1751                     }
1752                 }
1753             } else if (GTK_IS_TOOLBAR(child)) {
1754                 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1755                 gtk_toolbar_set_orientation( toolbar, orientation );
1756                 if (GTK_IS_HANDLE_BOX(toolbox)) {
1757                     handleBox = GTK_HANDLE_BOX(toolbox);
1758                 }
1759             }
1760         }
1761     }
1763     if (handleBox) {
1764         gtk_handle_box_set_handle_position(handleBox, pos);
1765     }
1768 static void
1769 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1771     gchar const * descr =
1772         "<ui>"
1773         "  <toolbar name='ToolToolbar'>"
1774         "    <toolitem action='ToolSelector' />"
1775         "    <toolitem action='ToolNode' />"
1776         "    <toolitem action='ToolTweak' />"
1777         "    <toolitem action='ToolSpray' />"
1778         "    <toolitem action='ToolZoom' />"
1779         "    <toolitem action='ToolRect' />"
1780         "    <toolitem action='Tool3DBox' />"
1781         "    <toolitem action='ToolArc' />"
1782         "    <toolitem action='ToolStar' />"
1783         "    <toolitem action='ToolSpiral' />"
1784         "    <toolitem action='ToolPencil' />"
1785         "    <toolitem action='ToolPen' />"
1786         "    <toolitem action='ToolCalligraphic' />"
1787         "    <toolitem action='ToolEraser' />"
1788 //        "    <toolitem action='ToolLPETool' />"
1789         "    <toolitem action='ToolPaintBucket' />"
1790         "    <toolitem action='ToolText' />"
1791         "    <toolitem action='ToolConnector' />"
1792         "    <toolitem action='ToolGradient' />"
1793         "    <toolitem action='ToolDropper' />"
1794         "  </toolbar>"
1795         "</ui>";
1797     setupToolboxCommon( toolbox, desktop, descr,
1798                         "/ui/ToolToolbar",
1799                         "/toolbox/tools/small");
1802 static void
1803 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1805     gchar const *const tname = ( eventcontext
1806                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1807                                  : NULL );
1808     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1810     for (int i = 0 ; tools[i].type_name ; i++ ) {
1811         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1812         if ( act ) {
1813             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1814             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1815             if ( verbAct ) {
1816                 verbAct->set_active(setActive);
1817             }
1818         }
1819     }
1822 static void
1823 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1825     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1826     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1827     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1828     GtkUIManager* mgr = gtk_ui_manager_new();
1829     GError* errVal = 0;
1830     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1831     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1833     std::map<std::string, GtkWidget*> dataHolders;
1835     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1836         if ( aux_toolboxes[i].prep_func ) {
1837             // converted to GtkActions and UIManager
1839             GtkWidget* kludge = gtk_toolbar_new();
1840             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1841             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1842             dataHolders[aux_toolboxes[i].type_name] = kludge;
1843             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1844         } else {
1846             GtkWidget *sub_toolbox = 0;
1847             if (aux_toolboxes[i].create_func == NULL) {
1848                 sub_toolbox = sp_empty_toolbox_new(desktop);
1849             } else {
1850                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1851             }
1853             gtk_size_group_add_widget( grouper, sub_toolbox );
1855             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1856             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1858         }
1859     }
1861     // Second pass to create toolbars *after* all GtkActions are created
1862     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1863         if ( aux_toolboxes[i].prep_func ) {
1864             // converted to GtkActions and UIManager
1866             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1868             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1869             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1871             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1872             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1873             g_free( tmp );
1874             tmp = 0;
1876             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1877                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1878             }
1880             Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1881             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1883             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1885             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1886                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1887                 swatch->setDesktop( desktop );
1888                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1889                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1890                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1891                 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 );
1892             }
1894             gtk_widget_show_all( holder );
1895             sp_set_font_size_smaller( holder );
1897             gtk_size_group_add_widget( grouper, holder );
1899             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1900             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1901         }
1902     }
1904     g_object_unref( G_OBJECT(grouper) );
1907 static void
1908 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1910     gchar const *tname = ( eventcontext
1911                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1912                            : NULL );
1913     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1914         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1915         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1916             gtk_widget_show_all(sub_toolbox);
1917             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1918         } else {
1919             gtk_widget_hide(sub_toolbox);
1920         }
1921     }
1924 static void
1925 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1927     gchar const * descr =
1928         "<ui>"
1929         "  <toolbar name='CommandsToolbar'>"
1930         "    <toolitem action='FileNew' />"
1931         "    <toolitem action='FileOpen' />"
1932         "    <toolitem action='FileSave' />"
1933         "    <toolitem action='FilePrint' />"
1934         "    <separator />"
1935         "    <toolitem action='FileImport' />"
1936         "    <toolitem action='FileExport' />"
1937         "    <separator />"
1938         "    <toolitem action='EditUndo' />"
1939         "    <toolitem action='EditRedo' />"
1940         "    <separator />"
1941         "    <toolitem action='EditCopy' />"
1942         "    <toolitem action='EditCut' />"
1943         "    <toolitem action='EditPaste' />"
1944         "    <separator />"
1945         "    <toolitem action='ZoomSelection' />"
1946         "    <toolitem action='ZoomDrawing' />"
1947         "    <toolitem action='ZoomPage' />"
1948         "    <separator />"
1949         "    <toolitem action='EditDuplicate' />"
1950         "    <toolitem action='EditClone' />"
1951         "    <toolitem action='EditUnlinkClone' />"
1952         "    <separator />"
1953         "    <toolitem action='SelectionGroup' />"
1954         "    <toolitem action='SelectionUnGroup' />"
1955         "    <separator />"
1956         "    <toolitem action='DialogFillStroke' />"
1957         "    <toolitem action='DialogText' />"
1958         "    <toolitem action='DialogLayers' />"
1959         "    <toolitem action='DialogXMLEditor' />"
1960         "    <toolitem action='DialogAlignDistribute' />"
1961         "    <separator />"
1962         "    <toolitem action='DialogPreferences' />"
1963         "    <toolitem action='DialogDocumentProperties' />"
1964 #if ENABLE_TASK_SUPPORT
1965         "    <separator />"
1966         "    <toolitem action='TaskSetAction' />"
1967 #endif // ENABLE_TASK_SUPPORT
1968         "  </toolbar>"
1969         "</ui>";
1971     setupToolboxCommon( toolbox, desktop, descr,
1972                         "/ui/CommandsToolbar",
1973                         "/toolbox/small" );
1976 static void
1977 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1981 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1983     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1984         return;
1985     }
1987     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1988     g_assert(ptr != NULL);
1990     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1991     SPNamedView *nv = sp_desktop_namedview(dt);
1992     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1994     if (dt == NULL || nv == NULL) {
1995         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1996         return;
1997     }
1999     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
2001     if (repr == NULL) {
2002         g_warning("This namedview doesn't have a xml representation attached!");
2003         return;
2004     }
2006     bool saved = sp_document_get_undo_sensitive(doc);
2007     sp_document_set_undo_sensitive(doc, false);
2009     bool v = false;
2010     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
2012     switch (attr) {
2013         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
2014             dt->toggleSnapGlobal();
2015             break;
2016         case SP_ATTR_INKSCAPE_SNAP_BBOX:
2017             v = nv->snap_manager.snapprefs.getSnapModeBBox();
2018             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
2019             break;
2020         case SP_ATTR_INKSCAPE_BBOX_PATHS:
2021             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
2022             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
2023             break;
2024         case SP_ATTR_INKSCAPE_BBOX_NODES:
2025             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
2026             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
2027             break;
2028         case SP_ATTR_INKSCAPE_SNAP_NODES:
2029             v = nv->snap_manager.snapprefs.getSnapModeNode();
2030             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
2031             break;
2032         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
2033             v = nv->snap_manager.snapprefs.getSnapToItemPath();
2034             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
2035             break;
2036         case SP_ATTR_INKSCAPE_OBJECT_NODES:
2037             v = nv->snap_manager.snapprefs.getSnapToItemNode();
2038             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
2039             break;
2040         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
2041             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
2042             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2043             break;
2044         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2045             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2046             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2047             break;
2048         case SP_ATTR_INKSCAPE_SNAP_CENTER:
2049             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2050             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2051             break;
2052         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2053             v = nv->snap_manager.snapprefs.getSnapToGrids();
2054             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2055             break;
2056         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2057             v = nv->snap_manager.snapprefs.getSnapToGuides();
2058             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2059             break;
2060         case SP_ATTR_INKSCAPE_SNAP_PAGE:
2061             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2062             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2063             break;
2064             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2065               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2066               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2067               break;*/
2068         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2069             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2070             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2071             break;
2072         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2073             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2074             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2075             break;
2076         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2077             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2078             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2079             break;
2080         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2081             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2082             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2083             break;
2084         default:
2085             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2086             break;
2087     }
2089     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2090     doc->setModifiedSinceSave();
2092     sp_document_set_undo_sensitive(doc, saved);
2095 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2097     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2099     gchar const * descr =
2100         "<ui>"
2101         "  <toolbar name='SnapToolbar'>"
2102         "    <toolitem action='ToggleSnapGlobal' />"
2103         "    <separator />"
2104         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2105         "    <toolitem action='ToggleSnapToBBoxPath' />"
2106         "    <toolitem action='ToggleSnapToBBoxNode' />"
2107         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2108         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2109         "    <separator />"
2110         "    <toolitem action='ToggleSnapFromNode' />"
2111         "    <toolitem action='ToggleSnapToItemPath' />"
2112         "    <toolitem action='ToggleSnapToPathIntersections' />"
2113         "    <toolitem action='ToggleSnapToItemNode' />"
2114         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2115         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2116         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2117         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2118         "    <separator />"
2119         "    <toolitem action='ToggleSnapToPageBorder' />"
2120         "    <toolitem action='ToggleSnapToGrids' />"
2121         "    <toolitem action='ToggleSnapToGuides' />"
2122         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2123         "  </toolbar>"
2124         "</ui>";
2126     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2128     {
2129         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2130                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2131                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2133         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2134         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2135     }
2137     {
2138         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2139                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2140                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2142         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2143         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2144     }
2146     {
2147         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2148                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2149                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2151         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2152         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2153     }
2155     {
2156         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2157                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2158                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2160         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2161         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2162     }
2164     {
2165         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2166                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2167                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2168                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2170         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2171         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2172     }
2174     {
2175         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2176                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2177                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2179         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2180         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2181     }
2183     {
2184         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2185                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2187         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2188         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2189     }
2191     {
2192         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2193                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2194                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2196         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2197         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2198     }
2200     {
2201         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2202                                                      _("Path intersections"), _("Snap to path intersections"),
2203                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2205         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2206         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2207     }
2209     {
2210         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2211                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2212                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2214         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2215         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2216     }
2218     {
2219         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2220                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2221                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2223         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2224         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2225     }
2227     {
2228         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2229                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2230                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2232         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2233         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2234     }
2236     {
2237         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2238                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2239                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2241         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2242         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2243     }
2245     {
2246         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2247                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2248                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2250         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2251         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2252     }
2254     {
2255         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2256                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2257                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2259         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2260         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2261     }
2263     {
2264         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2265                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2266                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2268         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2269         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2270     }
2272     {
2273         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2274                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2275                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2277         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2278         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2279     }
2281     /*{
2282       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2283       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2284       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2285       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2287       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2288       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2289       }*/
2291     setupToolboxCommon( toolbox, desktop, descr,
2292                         "/ui/SnapToolbar",
2293                         "/toolbox/secondary" );
2296 Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox)
2298     Glib::ustring name;
2299     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
2300     switch(id) {
2301         case BAR_TOOL:
2302             name = "ToolToolbar";
2303             break;
2304         case BAR_AUX:
2305             name = "AuxToolbar";
2306             break;
2307         case BAR_COMMANDS:
2308             name = "CommandsToolbar";
2309             break;
2310         case BAR_SNAP:
2311             name = "SnapToolbar";
2312             break;
2313     }
2315     return name;
2318 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2320     g_assert(desktop != NULL);
2321     g_assert(toolbox != NULL);
2323     SPNamedView *nv = sp_desktop_namedview(desktop);
2324     if (nv == NULL) {
2325         g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2326         return;
2327     }
2329     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2331     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2332     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2333     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2334     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2335     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2336     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2337     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2338     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2339     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2340     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2341     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2342     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2343     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2344     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2345     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2346     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2347     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2348     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2351     if (!act1) {
2352         return; // The snap actions haven't been defined yet (might be the case during startup)
2353     }
2355     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2356     // changes in our document because we're only updating the UI;
2357     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2358     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2360     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2361     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2363     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2364     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2365     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2367     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2368     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2369     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2370     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2371     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2372     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2373     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2374     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2376     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2377     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2378     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2380     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2381     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2382     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2383     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2384     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2385     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2386     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2387     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2388     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2389     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2390     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2391     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2392     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2393     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2394     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2396     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2397     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2398     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2399     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2401     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2402     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2403     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2404     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2407     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2410 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2412     gtk_widget_show(toolbox_toplevel);
2413     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2415     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2416     if (!shown_toolbox) {
2417         return;
2418     }
2419     gtk_widget_show(toolbox);
2421     gtk_widget_show_all(shown_toolbox);
2424 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2426     GtkWidget *tbl = gtk_toolbar_new();
2427     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2428     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2430     gtk_widget_show_all(tbl);
2431     sp_set_font_size_smaller (tbl);
2433     return tbl;
2436 #define MODE_LABEL_WIDTH 70
2438 //########################
2439 //##       Star         ##
2440 //########################
2442 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2444     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2446     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2447         // do not remember prefs if this call is initiated by an undo change, because undoing object
2448         // creation sets bogus values to its attributes before it is deleted
2449         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2450         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2451     }
2453     // quit if run by the attr_changed listener
2454     if (g_object_get_data( dataKludge, "freeze" )) {
2455         return;
2456     }
2458     // in turn, prevent listener from responding
2459     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2461     bool modmade = false;
2463     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2464     GSList const *items = selection->itemList();
2465     for (; items != NULL; items = items->next) {
2466         if (SP_IS_STAR((SPItem *) items->data)) {
2467             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2468             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2469             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2470                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2471                                     + M_PI / (gint)adj->value));
2472             SP_OBJECT((SPItem *) items->data)->updateRepr();
2473             modmade = true;
2474         }
2475     }
2476     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2477                                    _("Star: Change number of corners"));
2479     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2482 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2484     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2486     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2487         if (!IS_NAN(adj->value)) {
2488             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2489             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2490         }
2491     }
2493     // quit if run by the attr_changed listener
2494     if (g_object_get_data( dataKludge, "freeze" )) {
2495         return;
2496     }
2498     // in turn, prevent listener from responding
2499     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2501     bool modmade = false;
2502     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2503     GSList const *items = selection->itemList();
2504     for (; items != NULL; items = items->next) {
2505         if (SP_IS_STAR((SPItem *) items->data)) {
2506             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2508             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2509             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2510             if (r2 < r1) {
2511                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2512             } else {
2513                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2514             }
2516             SP_OBJECT((SPItem *) items->data)->updateRepr();
2517             modmade = true;
2518         }
2519     }
2521     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2522                                    _("Star: Change spoke ratio"));
2524     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2527 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2529     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2530     bool flat = ege_select_one_action_get_active( act ) == 0;
2532     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2533         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2534         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2535     }
2537     // quit if run by the attr_changed listener
2538     if (g_object_get_data( dataKludge, "freeze" )) {
2539         return;
2540     }
2542     // in turn, prevent listener from responding
2543     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2545     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2546     GSList const *items = selection->itemList();
2547     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2548     bool modmade = false;
2550     if ( prop_action ) {
2551         gtk_action_set_sensitive( prop_action, !flat );
2552     }
2554     for (; items != NULL; items = items->next) {
2555         if (SP_IS_STAR((SPItem *) items->data)) {
2556             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2557             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2558             SP_OBJECT((SPItem *) items->data)->updateRepr();
2559             modmade = true;
2560         }
2561     }
2563     if (modmade) {
2564         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2565                          flat ? _("Make polygon") : _("Make star"));
2566     }
2568     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2571 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2573     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2575     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2576         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2577         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2578     }
2580     // quit if run by the attr_changed listener
2581     if (g_object_get_data( dataKludge, "freeze" )) {
2582         return;
2583     }
2585     // in turn, prevent listener from responding
2586     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2588     bool modmade = false;
2590     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2591     GSList const *items = selection->itemList();
2592     for (; items != NULL; items = items->next) {
2593         if (SP_IS_STAR((SPItem *) items->data)) {
2594             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2595             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2596             SP_OBJECT(items->data)->updateRepr();
2597             modmade = true;
2598         }
2599     }
2600     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2601                                    _("Star: Change rounding"));
2603     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2606 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2608     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2610     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2611         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2612         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2613     }
2615     // quit if run by the attr_changed listener
2616     if (g_object_get_data( dataKludge, "freeze" )) {
2617         return;
2618     }
2620     // in turn, prevent listener from responding
2621     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2623     bool modmade = false;
2625     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2626     GSList const *items = selection->itemList();
2627     for (; items != NULL; items = items->next) {
2628         if (SP_IS_STAR((SPItem *) items->data)) {
2629             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2630             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2631             SP_OBJECT(items->data)->updateRepr();
2632             modmade = true;
2633         }
2634     }
2635     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2636                                    _("Star: Change randomization"));
2638     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2642 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2643                                        gchar const */*old_value*/, gchar const */*new_value*/,
2644                                        bool /*is_interactive*/, gpointer data)
2646     GtkWidget *tbl = GTK_WIDGET(data);
2648     // quit if run by the _changed callbacks
2649     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2650         return;
2651     }
2653     // in turn, prevent callbacks from responding
2654     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2656     GtkAdjustment *adj = 0;
2658     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2659     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2661     if (!strcmp(name, "inkscape:randomized")) {
2662         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2663         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2664     } else if (!strcmp(name, "inkscape:rounded")) {
2665         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2666         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2667     } else if (!strcmp(name, "inkscape:flatsided")) {
2668         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2669         char const *flatsides = repr->attribute("inkscape:flatsided");
2670         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2671         if ( flatsides && !strcmp(flatsides,"false") ) {
2672             ege_select_one_action_set_active( flat_action, 1 );
2673             gtk_action_set_sensitive( prop_action, TRUE );
2674         } else {
2675             ege_select_one_action_set_active( flat_action, 0 );
2676             gtk_action_set_sensitive( prop_action, FALSE );
2677         }
2678     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2679         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2680         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2681         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2682         if (r2 < r1) {
2683             gtk_adjustment_set_value(adj, r2/r1);
2684         } else {
2685             gtk_adjustment_set_value(adj, r1/r2);
2686         }
2687     } else if (!strcmp(name, "sodipodi:sides")) {
2688         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2689         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2690     }
2692     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2696 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2698     NULL, /* child_added */
2699     NULL, /* child_removed */
2700     star_tb_event_attr_changed,
2701     NULL, /* content_changed */
2702     NULL  /* order_changed */
2703 };
2706 /**
2707  *  \param selection Should not be NULL.
2708  */
2709 static void
2710 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2712     int n_selected = 0;
2713     Inkscape::XML::Node *repr = NULL;
2715     purge_repr_listener( tbl, tbl );
2717     for (GSList const *items = selection->itemList();
2718          items != NULL;
2719          items = items->next)
2720     {
2721         if (SP_IS_STAR((SPItem *) items->data)) {
2722             n_selected++;
2723             repr = SP_OBJECT_REPR((SPItem *) items->data);
2724         }
2725     }
2727     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2729     if (n_selected == 0) {
2730         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2731     } else if (n_selected == 1) {
2732         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2734         if (repr) {
2735             g_object_set_data( tbl, "repr", repr );
2736             Inkscape::GC::anchor(repr);
2737             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2738             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2739         }
2740     } else {
2741         // FIXME: implement averaging of all parameters for multiple selected stars
2742         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2743         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2744     }
2748 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2750     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2751     // callbacks to lump all the changes for all selected objects in one undo step
2753     GtkAdjustment *adj = 0;
2755     // fixme: make settable in prefs!
2756     gint mag = 5;
2757     gdouble prop = 0.5;
2758     gboolean flat = FALSE;
2759     gdouble randomized = 0;
2760     gdouble rounded = 0;
2762     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2763     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2765     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2766     gtk_action_set_sensitive( sb2, !flat );
2768     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2769     gtk_adjustment_set_value(adj, mag);
2770     gtk_adjustment_value_changed(adj);
2772     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2773     gtk_adjustment_set_value(adj, prop);
2774     gtk_adjustment_value_changed(adj);
2776     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2777     gtk_adjustment_set_value(adj, rounded);
2778     gtk_adjustment_value_changed(adj);
2780     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2781     gtk_adjustment_set_value(adj, randomized);
2782     gtk_adjustment_value_changed(adj);
2786 void
2787 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2789     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2790     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2791     GtkWidget *l = gtk_label_new(NULL);
2792     gtk_label_set_markup(GTK_LABEL(l), title);
2793     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2794     if ( GTK_IS_TOOLBAR(tbl) ) {
2795         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2796     } else {
2797         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2798     }
2799     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2803 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2805     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2807     {
2808         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2809         ege_output_action_set_use_markup( act, TRUE );
2810         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2811         g_object_set_data( holder, "mode_action", act );
2812     }
2814     {
2815         EgeAdjustmentAction* eact = 0;
2816         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2817         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2819         /* Flatsided checkbox */
2820         {
2821             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2823             GtkTreeIter iter;
2824             gtk_list_store_append( model, &iter );
2825             gtk_list_store_set( model, &iter,
2826                                 0, _("Polygon"),
2827                                 1, _("Regular polygon (with one handle) instead of a star"),
2828                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2829                                 -1 );
2831             gtk_list_store_append( model, &iter );
2832             gtk_list_store_set( model, &iter,
2833                                 0, _("Star"),
2834                                 1, _("Star instead of a regular polygon (with one handle)"),
2835                                 2, INKSCAPE_ICON_DRAW_STAR,
2836                                 -1 );
2838             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2839             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2840             g_object_set_data( holder, "flat_action", act );
2842             ege_select_one_action_set_appearance( act, "full" );
2843             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2844             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2845             ege_select_one_action_set_icon_column( act, 2 );
2846             ege_select_one_action_set_icon_size( act, secondarySize );
2847             ege_select_one_action_set_tooltip_column( act, 1  );
2849             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2850             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2851         }
2853         /* Magnitude */
2854         {
2855         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2856         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2857         eact = create_adjustment_action( "MagnitudeAction",
2858                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2859                                          "/tools/shapes/star/magnitude", 3,
2860                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2861                                          3, 1024, 1, 5,
2862                                          labels, values, G_N_ELEMENTS(labels),
2863                                          sp_stb_magnitude_value_changed,
2864                                          1.0, 0 );
2865         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2866         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2867         }
2869         /* Spoke ratio */
2870         {
2871         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2872         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2873         eact = create_adjustment_action( "SpokeAction",
2874                                          _("Spoke ratio"), _("Spoke ratio:"),
2875                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2876                                          // Base radius is the same for the closest handle.
2877                                          _("Base radius to tip radius ratio"),
2878                                          "/tools/shapes/star/proportion", 0.5,
2879                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2880                                          0.01, 1.0, 0.01, 0.1,
2881                                          labels, values, G_N_ELEMENTS(labels),
2882                                          sp_stb_proportion_value_changed );
2883         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2884         g_object_set_data( holder, "prop_action", eact );
2885         }
2887         if ( !isFlatSided ) {
2888             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2889         } else {
2890             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2891         }
2893         /* Roundedness */
2894         {
2895         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2896         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2897         eact = create_adjustment_action( "RoundednessAction",
2898                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2899                                          "/tools/shapes/star/rounded", 0.0,
2900                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2901                                          -10.0, 10.0, 0.01, 0.1,
2902                                          labels, values, G_N_ELEMENTS(labels),
2903                                          sp_stb_rounded_value_changed );
2904         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2905         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2906         }
2908         /* Randomization */
2909         {
2910         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2911         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2912         eact = create_adjustment_action( "RandomizationAction",
2913                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2914                                          "/tools/shapes/star/randomized", 0.0,
2915                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2916                                          -10.0, 10.0, 0.001, 0.01,
2917                                          labels, values, G_N_ELEMENTS(labels),
2918                                          sp_stb_randomized_value_changed, 0.1, 3 );
2919         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2920         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2921         }
2922     }
2924     {
2925         /* Reset */
2926         {
2927             GtkAction* act = gtk_action_new( "StarResetAction",
2928                                              _("Defaults"),
2929                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2930                                              GTK_STOCK_CLEAR );
2931             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2932             gtk_action_group_add_action( mainActions, act );
2933             gtk_action_set_sensitive( act, TRUE );
2934         }
2935     }
2937     sigc::connection *connection = new sigc::connection(
2938         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2939         );
2940     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2941     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2945 //########################
2946 //##       Rect         ##
2947 //########################
2949 static void sp_rtb_sensitivize( GObject *tbl )
2951     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2952     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2953     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2955     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2956         gtk_action_set_sensitive( not_rounded, FALSE );
2957     } else {
2958         gtk_action_set_sensitive( not_rounded, TRUE );
2959     }
2963 static void
2964 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2965                           void (*setter)(SPRect *, gdouble))
2967     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2969     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2970     SPUnit const *unit = tracker->getActiveUnit();
2972     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2973         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2974         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2975     }
2977     // quit if run by the attr_changed listener
2978     if (g_object_get_data( tbl, "freeze" )) {
2979         return;
2980     }
2982     // in turn, prevent listener from responding
2983     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2985     bool modmade = false;
2986     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2987     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2988         if (SP_IS_RECT(items->data)) {
2989             if (adj->value != 0) {
2990                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2991             } else {
2992                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2993             }
2994             modmade = true;
2995         }
2996     }
2998     sp_rtb_sensitivize( tbl );
3000     if (modmade) {
3001         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
3002                                    _("Change rectangle"));
3003     }
3005     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3008 static void
3009 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
3011     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
3014 static void
3015 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
3017     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
3020 static void
3021 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
3023     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
3026 static void
3027 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
3029     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
3034 static void
3035 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
3037     GtkAdjustment *adj = 0;
3039     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
3040     gtk_adjustment_set_value(adj, 0.0);
3041     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
3042     gtk_adjustment_value_changed(adj);
3044     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
3045     gtk_adjustment_set_value(adj, 0.0);
3046     gtk_adjustment_value_changed(adj);
3048     sp_rtb_sensitivize( obj );
3051 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
3052                                        gchar const */*old_value*/, gchar const */*new_value*/,
3053                                        bool /*is_interactive*/, gpointer data)
3055     GObject *tbl = G_OBJECT(data);
3057     // quit if run by the _changed callbacks
3058     if (g_object_get_data( tbl, "freeze" )) {
3059         return;
3060     }
3062     // in turn, prevent callbacks from responding
3063     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3065     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3066     SPUnit const *unit = tracker->getActiveUnit();
3068     gpointer item = g_object_get_data( tbl, "item" );
3069     if (item && SP_IS_RECT(item)) {
3070         {
3071             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3072             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3073             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3074         }
3076         {
3077             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3078             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3079             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3080         }
3082         {
3083             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3084             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3085             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3086         }
3088         {
3089             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3090             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3091             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3092         }
3093     }
3095     sp_rtb_sensitivize( tbl );
3097     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3101 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3102     NULL, /* child_added */
3103     NULL, /* child_removed */
3104     rect_tb_event_attr_changed,
3105     NULL, /* content_changed */
3106     NULL  /* order_changed */
3107 };
3109 /**
3110  *  \param selection should not be NULL.
3111  */
3112 static void
3113 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3115     int n_selected = 0;
3116     Inkscape::XML::Node *repr = NULL;
3117     SPItem *item = NULL;
3119     if ( g_object_get_data( tbl, "repr" ) ) {
3120         g_object_set_data( tbl, "item", NULL );
3121     }
3122     purge_repr_listener( tbl, tbl );
3124     for (GSList const *items = selection->itemList();
3125          items != NULL;
3126          items = items->next) {
3127         if (SP_IS_RECT((SPItem *) items->data)) {
3128             n_selected++;
3129             item = (SPItem *) items->data;
3130             repr = SP_OBJECT_REPR(item);
3131         }
3132     }
3134     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3136     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3138     if (n_selected == 0) {
3139         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3141         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3142         gtk_action_set_sensitive(w, FALSE);
3143         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3144         gtk_action_set_sensitive(h, FALSE);
3146     } else if (n_selected == 1) {
3147         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3148         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3150         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3151         gtk_action_set_sensitive(w, TRUE);
3152         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3153         gtk_action_set_sensitive(h, TRUE);
3155         if (repr) {
3156             g_object_set_data( tbl, "repr", repr );
3157             g_object_set_data( tbl, "item", item );
3158             Inkscape::GC::anchor(repr);
3159             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3160             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3161         }
3162     } else {
3163         // FIXME: implement averaging of all parameters for multiple selected
3164         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3165         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3166         sp_rtb_sensitivize( tbl );
3167     }
3171 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3173     EgeAdjustmentAction* eact = 0;
3174     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3176     {
3177         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3178         ege_output_action_set_use_markup( act, TRUE );
3179         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3180         g_object_set_data( holder, "mode_action", act );
3181     }
3183     // rx/ry units menu: create
3184     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3185     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3186     // fixme: add % meaning per cent of the width/height
3187     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3188     g_object_set_data( holder, "tracker", tracker );
3190     /* W */
3191     {
3192         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3193         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3194         eact = create_adjustment_action( "RectWidthAction",
3195                                          _("Width"), _("W:"), _("Width of rectangle"),
3196                                          "/tools/shapes/rect/width", 0,
3197                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3198                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3199                                          labels, values, G_N_ELEMENTS(labels),
3200                                          sp_rtb_width_value_changed );
3201         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3202         g_object_set_data( holder, "width_action", eact );
3203         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3204         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3205     }
3207     /* H */
3208     {
3209         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3210         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3211         eact = create_adjustment_action( "RectHeightAction",
3212                                          _("Height"), _("H:"), _("Height of rectangle"),
3213                                          "/tools/shapes/rect/height", 0,
3214                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3215                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3216                                          labels, values, G_N_ELEMENTS(labels),
3217                                          sp_rtb_height_value_changed );
3218         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3219         g_object_set_data( holder, "height_action", eact );
3220         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3221         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3222     }
3224     /* rx */
3225     {
3226         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3227         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3228         eact = create_adjustment_action( "RadiusXAction",
3229                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3230                                          "/tools/shapes/rect/rx", 0,
3231                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3232                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3233                                          labels, values, G_N_ELEMENTS(labels),
3234                                          sp_rtb_rx_value_changed);
3235         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3236         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3237     }
3239     /* ry */
3240     {
3241         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3242         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3243         eact = create_adjustment_action( "RadiusYAction",
3244                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3245                                          "/tools/shapes/rect/ry", 0,
3246                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3247                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3248                                          labels, values, G_N_ELEMENTS(labels),
3249                                          sp_rtb_ry_value_changed);
3250         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3251         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3252     }
3254     // add the units menu
3255     {
3256         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3257         gtk_action_group_add_action( mainActions, act );
3258     }
3260     /* Reset */
3261     {
3262         InkAction* inky = ink_action_new( "RectResetAction",
3263                                           _("Not rounded"),
3264                                           _("Make corners sharp"),
3265                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3266                                           secondarySize );
3267         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3268         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3269         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3270         g_object_set_data( holder, "not_rounded", inky );
3271     }
3273     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3274     sp_rtb_sensitivize( holder );
3276     sigc::connection *connection = new sigc::connection(
3277         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3278         );
3279     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3280     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3283 //########################
3284 //##       3D Box       ##
3285 //########################
3287 // normalize angle so that it lies in the interval [0,360]
3288 static double box3d_normalize_angle (double a) {
3289     double angle = a + ((int) (a/360.0))*360;
3290     if (angle < 0) {
3291         angle += 360.0;
3292     }
3293     return angle;
3296 static void
3297 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3298                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3299     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3300     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3301     //       are reset).
3302     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3304     if (is_infinite) {
3305         gtk_toggle_action_set_active(tact, TRUE);
3306         gtk_action_set_sensitive(act, TRUE);
3308         double angle = persp3d_get_infinite_angle(persp, axis);
3309         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3310             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3311         }
3312     } else {
3313         gtk_toggle_action_set_active(tact, FALSE);
3314         gtk_action_set_sensitive(act, FALSE);
3315     }
3318 static void
3319 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3320     if (!persp_repr) {
3321         g_print ("No perspective given to box3d_resync_toolbar().\n");
3322         return;
3323     }
3325     GtkWidget *tbl = GTK_WIDGET(data);
3326     GtkAdjustment *adj = 0;
3327     GtkAction *act = 0;
3328     GtkToggleAction *tact = 0;
3329     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3330     if (!persp) {
3331         // Hmm, is it an error if this happens?
3332         return;
3333     }
3334     {
3335         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3336         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3337         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3339         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3340     }
3341     {
3342         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3343         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3344         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3346         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3347     }
3348     {
3349         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3350         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3351         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3353         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3354     }
3357 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3358                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3359                                                   bool /*is_interactive*/, gpointer data)
3361     GtkWidget *tbl = GTK_WIDGET(data);
3363     // quit if run by the attr_changed or selection changed listener
3364     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3365         return;
3366     }
3368     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3369     // sp_document_maybe_done() when the document is undo insensitive)
3370     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3372     // TODO: Only update the appropriate part of the toolbar
3373 //    if (!strcmp(name, "inkscape:vp_z")) {
3374         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3375 //    }
3377     Persp3D *persp = persp3d_get_from_repr(repr);
3378     persp3d_update_box_reprs(persp);
3380     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3383 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3385     NULL, /* child_added */
3386     NULL, /* child_removed */
3387     box3d_persp_tb_event_attr_changed,
3388     NULL, /* content_changed */
3389     NULL  /* order_changed */
3390 };
3392 /**
3393  *  \param selection Should not be NULL.
3394  */
3395 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3396 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3397 static void
3398 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3400     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3401     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3402     // update the perspectives with infinite VPs and leave the other ones untouched).
3404     Inkscape::XML::Node *persp_repr = NULL;
3405     purge_repr_listener(tbl, tbl);
3407     SPItem *item = selection->singleItem();
3408     if (item && SP_IS_BOX3D(item)) {
3409         // FIXME: Also deal with multiple selected boxes
3410         SPBox3D *box = SP_BOX3D(item);
3411         Persp3D *persp = box3d_get_perspective(box);
3412         persp_repr = SP_OBJECT_REPR(persp);
3413         if (persp_repr) {
3414             g_object_set_data(tbl, "repr", persp_repr);
3415             Inkscape::GC::anchor(persp_repr);
3416             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3417             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3418         }
3420         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3421         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3422         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3424         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3425         box3d_resync_toolbar(persp_repr, tbl);
3426         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3427     }
3430 static void
3431 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3433     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3434     SPDocument *document = sp_desktop_document(desktop);
3436     // quit if run by the attr_changed or selection changed listener
3437     if (g_object_get_data( dataKludge, "freeze" )) {
3438         return;
3439     }
3441     // in turn, prevent listener from responding
3442     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3444     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3445     if (sel_persps.empty()) {
3446         // this can happen when the document is created; we silently ignore it
3447         return;
3448     }
3449     Persp3D *persp = sel_persps.front();
3451     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3452     SP_OBJECT(persp)->updateRepr();
3454     // TODO: use the correct axis here, too
3455     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3457     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3461 static void
3462 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3464     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3467 static void
3468 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3470     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3473 static void
3474 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3476     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3480 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3482     // TODO: Take all selected perspectives into account
3483     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3484     if (sel_persps.empty()) {
3485         // this can happen when the document is created; we silently ignore it
3486         return;
3487     }
3488     Persp3D *persp = sel_persps.front();
3490     bool set_infinite = gtk_toggle_action_get_active(act);
3491     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3494 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3496     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3499 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3501     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3504 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3506     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3509 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3511     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3512     EgeAdjustmentAction* eact = 0;
3513     SPDocument *document = sp_desktop_document (desktop);
3514     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3516     EgeAdjustmentAction* box3d_angle_x = 0;
3517     EgeAdjustmentAction* box3d_angle_y = 0;
3518     EgeAdjustmentAction* box3d_angle_z = 0;
3520     /* Angle X */
3521     {
3522         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3523         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3524         eact = create_adjustment_action( "3DBoxAngleXAction",
3525                                          _("Angle in X direction"), _("Angle X:"),
3526                                          // Translators: PL is short for 'perspective line'
3527                                          _("Angle of PLs in X direction"),
3528                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3529                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3530                                          -360.0, 360.0, 1.0, 10.0,
3531                                          labels, values, G_N_ELEMENTS(labels),
3532                                          box3d_angle_x_value_changed );
3533         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3534         g_object_set_data( holder, "box3d_angle_x_action", eact );
3535         box3d_angle_x = eact;
3536     }
3538     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3539         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3540     } else {
3541         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3542     }
3545     /* VP X state */
3546     {
3547         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3548                                                       // Translators: VP is short for 'vanishing point'
3549                                                       _("State of VP in X direction"),
3550                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3551                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3552                                                       Inkscape::ICON_SIZE_DECORATION );
3553         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3554         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3555         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3556         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3557         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3558     }
3560     /* Angle Y */
3561     {
3562         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3563         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3564         eact = create_adjustment_action( "3DBoxAngleYAction",
3565                                          _("Angle in Y direction"), _("Angle Y:"),
3566                                          // Translators: PL is short for 'perspective line'
3567                                          _("Angle of PLs in Y direction"),
3568                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3569                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3570                                          -360.0, 360.0, 1.0, 10.0,
3571                                          labels, values, G_N_ELEMENTS(labels),
3572                                          box3d_angle_y_value_changed );
3573         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3574         g_object_set_data( holder, "box3d_angle_y_action", eact );
3575         box3d_angle_y = eact;
3576     }
3578     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3579         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3580     } else {
3581         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3582     }
3584     /* VP Y state */
3585     {
3586         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3587                                                       // Translators: VP is short for 'vanishing point'
3588                                                       _("State of VP in Y direction"),
3589                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3590                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3591                                                       Inkscape::ICON_SIZE_DECORATION );
3592         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3593         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3594         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3595         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3596         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3597     }
3599     /* Angle Z */
3600     {
3601         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3602         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3603         eact = create_adjustment_action( "3DBoxAngleZAction",
3604                                          _("Angle in Z direction"), _("Angle Z:"),
3605                                          // Translators: PL is short for 'perspective line'
3606                                          _("Angle of PLs in Z direction"),
3607                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3608                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3609                                          -360.0, 360.0, 1.0, 10.0,
3610                                          labels, values, G_N_ELEMENTS(labels),
3611                                          box3d_angle_z_value_changed );
3612         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3613         g_object_set_data( holder, "box3d_angle_z_action", eact );
3614         box3d_angle_z = eact;
3615     }
3617     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3618         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3619     } else {
3620         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3621     }
3623     /* VP Z state */
3624     {
3625         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3626                                                       // Translators: VP is short for 'vanishing point'
3627                                                       _("State of VP in Z direction"),
3628                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3629                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3630                                                       Inkscape::ICON_SIZE_DECORATION );
3631         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3632         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3633         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3634         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3635         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3636     }
3638     sigc::connection *connection = new sigc::connection(
3639         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3640        );
3641     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3642     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3645 //########################
3646 //##       Spiral       ##
3647 //########################
3649 static void
3650 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3652     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3654     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3655         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3656         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3657     }
3659     // quit if run by the attr_changed listener
3660     if (g_object_get_data( tbl, "freeze" )) {
3661         return;
3662     }
3664     // in turn, prevent listener from responding
3665     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3667     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3669     bool modmade = false;
3670     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3671          items != NULL;
3672          items = items->next)
3673     {
3674         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3675             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3676             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3677             SP_OBJECT((SPItem *) items->data)->updateRepr();
3678             modmade = true;
3679         }
3680     }
3682     g_free(namespaced_name);
3684     if (modmade) {
3685         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3686                                    _("Change spiral"));
3687     }
3689     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3692 static void
3693 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3695     sp_spl_tb_value_changed(adj, tbl, "revolution");
3698 static void
3699 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3701     sp_spl_tb_value_changed(adj, tbl, "expansion");
3704 static void
3705 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3707     sp_spl_tb_value_changed(adj, tbl, "t0");
3710 static void
3711 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3713     GtkWidget *tbl = GTK_WIDGET(obj);
3715     GtkAdjustment *adj;
3717     // fixme: make settable
3718     gdouble rev = 5;
3719     gdouble exp = 1.0;
3720     gdouble t0 = 0.0;
3722     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3723     gtk_adjustment_set_value(adj, rev);
3724     gtk_adjustment_value_changed(adj);
3726     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3727     gtk_adjustment_set_value(adj, exp);
3728     gtk_adjustment_value_changed(adj);
3730     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3731     gtk_adjustment_set_value(adj, t0);
3732     gtk_adjustment_value_changed(adj);
3734     spinbutton_defocus(GTK_OBJECT(tbl));
3738 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3739                                          gchar const */*old_value*/, gchar const */*new_value*/,
3740                                          bool /*is_interactive*/, gpointer data)
3742     GtkWidget *tbl = GTK_WIDGET(data);
3744     // quit if run by the _changed callbacks
3745     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3746         return;
3747     }
3749     // in turn, prevent callbacks from responding
3750     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3752     GtkAdjustment *adj;
3753     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3754     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3756     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3757     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3759     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3760     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3762     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3766 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3767     NULL, /* child_added */
3768     NULL, /* child_removed */
3769     spiral_tb_event_attr_changed,
3770     NULL, /* content_changed */
3771     NULL  /* order_changed */
3772 };
3774 static void
3775 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3777     int n_selected = 0;
3778     Inkscape::XML::Node *repr = NULL;
3780     purge_repr_listener( tbl, tbl );
3782     for (GSList const *items = selection->itemList();
3783          items != NULL;
3784          items = items->next)
3785     {
3786         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3787             n_selected++;
3788             repr = SP_OBJECT_REPR((SPItem *) items->data);
3789         }
3790     }
3792     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3794     if (n_selected == 0) {
3795         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3796     } else if (n_selected == 1) {
3797         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3799         if (repr) {
3800             g_object_set_data( tbl, "repr", repr );
3801             Inkscape::GC::anchor(repr);
3802             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3803             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3804         }
3805     } else {
3806         // FIXME: implement averaging of all parameters for multiple selected
3807         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3808         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3809     }
3813 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3815     EgeAdjustmentAction* eact = 0;
3816     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3818     {
3819         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3820         ege_output_action_set_use_markup( act, TRUE );
3821         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3822         g_object_set_data( holder, "mode_action", act );
3823     }
3825     /* Revolution */
3826     {
3827         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3828         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3829         eact = create_adjustment_action( "SpiralRevolutionAction",
3830                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3831                                          "/tools/shapes/spiral/revolution", 3.0,
3832                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3833                                          0.01, 1024.0, 0.1, 1.0,
3834                                          labels, values, G_N_ELEMENTS(labels),
3835                                          sp_spl_tb_revolution_value_changed, 1, 2);
3836         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3837     }
3839     /* Expansion */
3840     {
3841         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3842         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3843         eact = create_adjustment_action( "SpiralExpansionAction",
3844                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3845                                          "/tools/shapes/spiral/expansion", 1.0,
3846                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3847                                          0.0, 1000.0, 0.01, 1.0,
3848                                          labels, values, G_N_ELEMENTS(labels),
3849                                          sp_spl_tb_expansion_value_changed);
3850         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3851     }
3853     /* T0 */
3854     {
3855         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3856         gdouble values[] = {0, 0.5, 0.9};
3857         eact = create_adjustment_action( "SpiralT0Action",
3858                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3859                                          "/tools/shapes/spiral/t0", 0.0,
3860                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3861                                          0.0, 0.999, 0.01, 1.0,
3862                                          labels, values, G_N_ELEMENTS(labels),
3863                                          sp_spl_tb_t0_value_changed);
3864         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3865     }
3867     /* Reset */
3868     {
3869         InkAction* inky = ink_action_new( "SpiralResetAction",
3870                                           _("Defaults"),
3871                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3872                                           GTK_STOCK_CLEAR,
3873                                           secondarySize );
3874         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3875         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3876     }
3879     sigc::connection *connection = new sigc::connection(
3880         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3881         );
3882     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3883     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3886 //########################
3887 //##     Pen/Pencil     ##
3888 //########################
3890 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3891 static Glib::ustring const
3892 freehand_tool_name(GObject *dataKludge)
3894     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3895     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3896              ? "/tools/freehand/pen"
3897              : "/tools/freehand/pencil" );
3900 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3902     gint mode = ege_select_one_action_get_active(act);
3904     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3905     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3907     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3909     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3910     // preparatory work here
3911     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3912         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3913         sp_pen_context_set_polyline_mode(pc);
3914     }
3917 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3919     /* Freehand mode toggle buttons */
3920     {
3921         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3922         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3923         Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3925         {
3926             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3928             GtkTreeIter iter;
3929             gtk_list_store_append( model, &iter );
3930             gtk_list_store_set( model, &iter,
3931                                 0, _("Bezier"),
3932                                 1, _("Create regular Bezier path"),
3933                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3934                                 -1 );
3936             gtk_list_store_append( model, &iter );
3937             gtk_list_store_set( model, &iter,
3938                                 0, _("Spiro"),
3939                                 1, _("Create Spiro path"),
3940                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3941                                 -1 );
3943             if (!tool_is_pencil) {
3944                 gtk_list_store_append( model, &iter );
3945                 gtk_list_store_set( model, &iter,
3946                                     0, _("Zigzag"),
3947                                     1, _("Create a sequence of straight line segments"),
3948                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3949                                     -1 );
3951                 gtk_list_store_append( model, &iter );
3952                 gtk_list_store_set( model, &iter,
3953                                     0, _("Paraxial"),
3954                                     1, _("Create a sequence of paraxial line segments"),
3955                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3956                                     -1 );
3957             }
3959             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3960                                                                 "FreehandModeActionPencil" :
3961                                                                 "FreehandModeActionPen",
3962                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3963             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3965             ege_select_one_action_set_appearance( act, "full" );
3966             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3967             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3968             ege_select_one_action_set_icon_column( act, 2 );
3969             ege_select_one_action_set_icon_size( act, secondarySize );
3970             ege_select_one_action_set_tooltip_column( act, 1  );
3972             ege_select_one_action_set_active( act, freehandMode);
3973             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3974         }
3975     }
3978 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3979     gint shape = ege_select_one_action_get_active( act );
3980     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3981     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3984 /**
3985  * \brief Generate the list of freehand advanced shape option entries.
3986  */
3987 GList * freehand_shape_dropdown_items_list() {
3988     GList *glist = NULL;
3990     glist = g_list_append (glist, _("None"));
3991     glist = g_list_append (glist, _("Triangle in"));
3992     glist = g_list_append (glist, _("Triangle out"));
3993     glist = g_list_append (glist, _("Ellipse"));
3994     glist = g_list_append (glist, _("From clipboard"));
3996     return glist;
3999 static void
4000 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
4001     /*advanced shape options */
4002     {
4003         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4004         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
4006         GList* items = 0;
4007         gint count = 0;
4008         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
4009         {
4010             GtkTreeIter iter;
4011             gtk_list_store_append( model, &iter );
4012             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
4013             count++;
4014         }
4015         g_list_free( items );
4016         items = 0;
4017         EgeSelectOneAction* act1 = ege_select_one_action_new(
4018             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
4019             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
4020         g_object_set( act1, "short_label", _("Shape:"), NULL );
4021         ege_select_one_action_set_appearance( act1, "compact" );
4022         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
4023         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
4024         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4025         g_object_set_data( holder, "shape_action", act1 );
4026     }
4029 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4031     sp_add_freehand_mode_toggle(mainActions, holder, false);
4032     freehand_add_advanced_shape_options(mainActions, holder, false);
4036 static void
4037 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
4039     GtkWidget *tbl = GTK_WIDGET(obj);
4041     GtkAdjustment *adj;
4043     // fixme: make settable
4044     gdouble tolerance = 4;
4046     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
4047     gtk_adjustment_set_value(adj, tolerance);
4048     gtk_adjustment_value_changed(adj);
4050     spinbutton_defocus(GTK_OBJECT(tbl));
4053 static void
4054 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
4056     // quit if run by the attr_changed listener
4057     if (g_object_get_data( tbl, "freeze" )) {
4058         return;
4059     }
4060     // in turn, prevent listener from responding
4061     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4062     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4063     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4064     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4067 /*
4068 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4069 public:
4070     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4071     {
4072         g_object_set_data(_obj, "prefobserver", this);
4073     }
4074     virtual ~PencilToleranceObserver() {
4075         if (g_object_get_data(_obj, "prefobserver") == this) {
4076             g_object_set_data(_obj, "prefobserver", NULL);
4077         }
4078     }
4079     virtual void notify(Inkscape::Preferences::Entry const &val) {
4080         GObject* tbl = _obj;
4081         if (g_object_get_data( tbl, "freeze" )) {
4082             return;
4083         }
4084         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4086         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4088         double v = val.getDouble(adj->value);
4089         gtk_adjustment_set_value(adj, v);
4090         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4091     }
4092 private:
4093     GObject *_obj;
4094 };
4095 */
4097 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4099     sp_add_freehand_mode_toggle(mainActions, holder, true);
4101     EgeAdjustmentAction* eact = 0;
4103     /* Tolerance */
4104     {
4105         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4106         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4107         eact = create_adjustment_action( "PencilToleranceAction",
4108                                          _("Smoothing:"), _("Smoothing: "),
4109                  _("How much smoothing (simplifying) is applied to the line"),
4110                                          "/tools/freehand/pencil/tolerance",
4111                                          3.0,
4112                                          GTK_WIDGET(desktop->canvas), NULL,
4113                                          holder, TRUE, "altx-pencil",
4114                                          1, 100.0, 0.5, 1.0,
4115                                          labels, values, G_N_ELEMENTS(labels),
4116                                          sp_pencil_tb_tolerance_value_changed,
4117                                          1, 2);
4118         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4119         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4120     }
4122     /* advanced shape options */
4123     freehand_add_advanced_shape_options(mainActions, holder, true);
4125     /* Reset */
4126     {
4127         InkAction* inky = ink_action_new( "PencilResetAction",
4128                                           _("Defaults"),
4129                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4130                                           GTK_STOCK_CLEAR,
4131                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4132         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4133         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4134     }
4136     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4141 //########################
4142 //##       Tweak        ##
4143 //########################
4145 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4147     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4148     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4151 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4153     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4154     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4157 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4159     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4160     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4163 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4165     int mode = ege_select_one_action_get_active( act );
4166     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4167     prefs->setInt("/tools/tweak/mode", mode);
4169     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4170     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4171     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4172     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4173     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4174     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4175     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4176         if (doh) gtk_action_set_sensitive (doh, TRUE);
4177         if (dos) gtk_action_set_sensitive (dos, TRUE);
4178         if (dol) gtk_action_set_sensitive (dol, TRUE);
4179         if (doo) gtk_action_set_sensitive (doo, TRUE);
4180         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4181         if (fid) gtk_action_set_sensitive (fid, FALSE);
4182     } else {
4183         if (doh) gtk_action_set_sensitive (doh, FALSE);
4184         if (dos) gtk_action_set_sensitive (dos, FALSE);
4185         if (dol) gtk_action_set_sensitive (dol, FALSE);
4186         if (doo) gtk_action_set_sensitive (doo, FALSE);
4187         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4188         if (fid) gtk_action_set_sensitive (fid, TRUE);
4189     }
4192 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4194     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4195     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4198 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4199     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4200     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4202 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4203     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4204     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4206 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4207     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4208     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4210 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4211     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4212     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4215 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4217     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4218     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4220     {
4221         /* Width */
4222         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4223         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4224         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4225                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4226                                                               "/tools/tweak/width", 15,
4227                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4228                                                               1, 100, 1.0, 10.0,
4229                                                               labels, values, G_N_ELEMENTS(labels),
4230                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4231         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4232         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4233         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4234     }
4237     {
4238         /* Force */
4239         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4240         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4241         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4242                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4243                                                               "/tools/tweak/force", 20,
4244                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4245                                                               1, 100, 1.0, 10.0,
4246                                                               labels, values, G_N_ELEMENTS(labels),
4247                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4248         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4249         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4250         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4251     }
4253     /* Mode */
4254     {
4255         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4257         GtkTreeIter iter;
4258         gtk_list_store_append( model, &iter );
4259         gtk_list_store_set( model, &iter,
4260                             0, _("Move mode"),
4261                             1, _("Move objects in any direction"),
4262                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4263                             -1 );
4265         gtk_list_store_append( model, &iter );
4266         gtk_list_store_set( model, &iter,
4267                             0, _("Move in/out mode"),
4268                             1, _("Move objects towards cursor; with Shift from cursor"),
4269                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4270                             -1 );
4272         gtk_list_store_append( model, &iter );
4273         gtk_list_store_set( model, &iter,
4274                             0, _("Move jitter mode"),
4275                             1, _("Move objects in random directions"),
4276                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4277                             -1 );
4279         gtk_list_store_append( model, &iter );
4280         gtk_list_store_set( model, &iter,
4281                             0, _("Scale mode"),
4282                             1, _("Shrink objects, with Shift enlarge"),
4283                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4284                             -1 );
4286         gtk_list_store_append( model, &iter );
4287         gtk_list_store_set( model, &iter,
4288                             0, _("Rotate mode"),
4289                             1, _("Rotate objects, with Shift counterclockwise"),
4290                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4291                             -1 );
4293         gtk_list_store_append( model, &iter );
4294         gtk_list_store_set( model, &iter,
4295                             0, _("Duplicate/delete mode"),
4296                             1, _("Duplicate objects, with Shift delete"),
4297                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4298                             -1 );
4300         gtk_list_store_append( model, &iter );
4301         gtk_list_store_set( model, &iter,
4302                             0, _("Push mode"),
4303                             1, _("Push parts of paths in any direction"),
4304                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4305                             -1 );
4307         gtk_list_store_append( model, &iter );
4308         gtk_list_store_set( model, &iter,
4309                             0, _("Shrink/grow mode"),
4310                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4311                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4312                             -1 );
4314         gtk_list_store_append( model, &iter );
4315         gtk_list_store_set( model, &iter,
4316                             0, _("Attract/repel mode"),
4317                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4318                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4319                             -1 );
4321         gtk_list_store_append( model, &iter );
4322         gtk_list_store_set( model, &iter,
4323                             0, _("Roughen mode"),
4324                             1, _("Roughen parts of paths"),
4325                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4326                             -1 );
4328         gtk_list_store_append( model, &iter );
4329         gtk_list_store_set( model, &iter,
4330                             0, _("Color paint mode"),
4331                             1, _("Paint the tool's color upon selected objects"),
4332                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4333                             -1 );
4335         gtk_list_store_append( model, &iter );
4336         gtk_list_store_set( model, &iter,
4337                             0, _("Color jitter mode"),
4338                             1, _("Jitter the colors of selected objects"),
4339                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4340                             -1 );
4342         gtk_list_store_append( model, &iter );
4343         gtk_list_store_set( model, &iter,
4344                             0, _("Blur mode"),
4345                             1, _("Blur selected objects more; with Shift, blur less"),
4346                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4347                             -1 );
4350         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4351         g_object_set( act, "short_label", _("Mode:"), NULL );
4352         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4353         g_object_set_data( holder, "mode_action", act );
4355         ege_select_one_action_set_appearance( act, "full" );
4356         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4357         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4358         ege_select_one_action_set_icon_column( act, 2 );
4359         ege_select_one_action_set_icon_size( act, secondarySize );
4360         ege_select_one_action_set_tooltip_column( act, 1  );
4362         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4363         ege_select_one_action_set_active( act, mode );
4364         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4366         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4367     }
4369     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4371     {
4372         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4373         ege_output_action_set_use_markup( act, TRUE );
4374         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4375         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4376             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4377         g_object_set_data( holder, "tweak_channels_label", act);
4378     }
4380     {
4381         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4382                                                       _("Hue"),
4383                                                       _("In color mode, act on objects' hue"),
4384                                                       NULL,
4385                                                       Inkscape::ICON_SIZE_DECORATION );
4386         //TRANSLATORS:  "H" here stands for hue
4387         g_object_set( act, "short_label", _("H"), NULL );
4388         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4389         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4390         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4391         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4392             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4393         g_object_set_data( holder, "tweak_doh", act);
4394     }
4395     {
4396         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4397                                                       _("Saturation"),
4398                                                       _("In color mode, act on objects' saturation"),
4399                                                       NULL,
4400                                                       Inkscape::ICON_SIZE_DECORATION );
4401         //TRANSLATORS: "S" here stands for Saturation
4402         g_object_set( act, "short_label", _("S"), NULL );
4403         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4404         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4405         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4406         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4407             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4408         g_object_set_data( holder, "tweak_dos", act );
4409     }
4410     {
4411         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4412                                                       _("Lightness"),
4413                                                       _("In color mode, act on objects' lightness"),
4414                                                       NULL,
4415                                                       Inkscape::ICON_SIZE_DECORATION );
4416         //TRANSLATORS: "L" here stands for Lightness
4417         g_object_set( act, "short_label", _("L"), NULL );
4418         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4419         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4420         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4421         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4422             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4423         g_object_set_data( holder, "tweak_dol", act );
4424     }
4425     {
4426         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4427                                                       _("Opacity"),
4428                                                       _("In color mode, act on objects' opacity"),
4429                                                       NULL,
4430                                                       Inkscape::ICON_SIZE_DECORATION );
4431         //TRANSLATORS: "O" here stands for Opacity
4432         g_object_set( act, "short_label", _("O"), NULL );
4433         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4434         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4435         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4436         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4437             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4438         g_object_set_data( holder, "tweak_doo", act );
4439     }
4441     {   /* Fidelity */
4442         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4443         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4444         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4445                                                               _("Fidelity"), _("Fidelity:"),
4446                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4447                                                               "/tools/tweak/fidelity", 50,
4448                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4449                                                               1, 100, 1.0, 10.0,
4450                                                               labels, values, G_N_ELEMENTS(labels),
4451                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4452         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4453         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4454         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4455             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4456         g_object_set_data( holder, "tweak_fidelity", eact );
4457     }
4460     /* Use Pressure button */
4461     {
4462         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4463                                                       _("Pressure"),
4464                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4465                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4466                                                       Inkscape::ICON_SIZE_DECORATION );
4467         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4468         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4469         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4470     }
4475 //########################
4476 //##       Spray        ##
4477 //########################
4479 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4481     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4482     prefs->setDouble( "/tools/spray/width", adj->value );
4485 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4487     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4488     prefs->setDouble( "/tools/spray/mean", adj->value );
4491 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4493     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4494     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4497 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4499     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4500     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4503 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4505     int mode = ege_select_one_action_get_active( act );
4506     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4507     prefs->setInt("/tools/spray/mode", mode);
4510 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4512     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4513     prefs->setDouble( "/tools/spray/population", adj->value );
4516 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4518     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4519     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4522 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4524     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4525     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4529 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4531     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4532     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4534     {
4535         /* Width */
4536         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4537         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4538         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4539                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4540                                                               "/tools/spray/width", 15,
4541                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4542                                                               1, 100, 1.0, 10.0,
4543                                                               labels, values, G_N_ELEMENTS(labels),
4544                                                               sp_spray_width_value_changed,  1, 0 );
4545         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4546         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4547         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4548     }
4550     {
4551         /* Mean */
4552         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4553         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4554         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4555                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4556                                                               "/tools/spray/mean", 0,
4557                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4558                                                               0, 100, 1.0, 10.0,
4559                                                               labels, values, G_N_ELEMENTS(labels),
4560                                                               sp_spray_mean_value_changed,  1, 0 );
4561         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4562         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4563         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4564     }
4566     {
4567         /* Standard_deviation */
4568         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4569         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4570         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4571                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4572                                                               "/tools/spray/standard_deviation", 70,
4573                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4574                                                               1, 100, 1.0, 10.0,
4575                                                               labels, values, G_N_ELEMENTS(labels),
4576                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4577         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4578         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4579         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4580     }
4582     /* Mode */
4583     {
4584         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4586         GtkTreeIter iter;
4587         gtk_list_store_append( model, &iter );
4588         gtk_list_store_set( model, &iter,
4589                             0, _("Spray with copies"),
4590                             1, _("Spray copies of the initial selection"),
4591                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4592                             -1 );
4594         gtk_list_store_append( model, &iter );
4595         gtk_list_store_set( model, &iter,
4596                             0, _("Spray with clones"),
4597                             1, _("Spray clones of the initial selection"),
4598                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4599                             -1 );
4601         gtk_list_store_append( model, &iter );
4602         gtk_list_store_set( model, &iter,
4603                             0, _("Spray single path"),
4604                             1, _("Spray objects in a single path"),
4605                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4606                             -1 );
4608         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4609         g_object_set( act, "short_label", _("Mode:"), NULL );
4610         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4611         g_object_set_data( holder, "mode_action", act );
4613         ege_select_one_action_set_appearance( act, "full" );
4614         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4615         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4616         ege_select_one_action_set_icon_column( act, 2 );
4617         ege_select_one_action_set_icon_size( act, secondarySize );
4618         ege_select_one_action_set_tooltip_column( act, 1  );
4620         gint mode = prefs->getInt("/tools/spray/mode", 1);
4621         ege_select_one_action_set_active( act, mode );
4622         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4624         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4625     }
4627     {   /* Population */
4628         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4629         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4630         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4631                                                               _("Amount"), _("Amount:"),
4632                                                               _("Adjusts the number of items sprayed per clic."),
4633                                                               "/tools/spray/population", 70,
4634                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4635                                                               1, 100, 1.0, 10.0,
4636                                                               labels, values, G_N_ELEMENTS(labels),
4637                                                               sp_spray_population_value_changed,  1, 0 );
4638         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4639         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4640         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4641         g_object_set_data( holder, "spray_population", eact );
4642     }
4644     /* Use Pressure button */
4645     {
4646         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4647                                                       _("Pressure"),
4648                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4649                                                       "use_pressure",
4650                                                       Inkscape::ICON_SIZE_DECORATION );
4651         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4652         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4653         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4654     }
4656     {   /* Rotation */
4657         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4658         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4659         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4660                                                               _("Rotation"), _("Rotation:"),
4661                                                               // xgettext:no-c-format
4662                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4663                                                               "/tools/spray/rotation_variation", 0,
4664                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4665                                                               0, 100, 1.0, 10.0,
4666                                                               labels, values, G_N_ELEMENTS(labels),
4667                                                               sp_spray_rotation_value_changed,  1, 0 );
4668         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4669         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4670         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4671         g_object_set_data( holder, "spray_rotation", eact );
4672     }
4674     {   /* Scale */
4675         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4676         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4677         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4678                                                               _("Scale"), _("Scale:"),
4679                                                               // xgettext:no-c-format
4680                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4681                                                               "/tools/spray/scale_variation", 0,
4682                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4683                                                               0, 100, 1.0, 10.0,
4684                                                               labels, values, G_N_ELEMENTS(labels),
4685                                                               sp_spray_scale_value_changed,  1, 0 );
4686         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4687         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4688         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4689         g_object_set_data( holder, "spray_scale", eact );
4690     }
4697 //########################
4698 //##     Calligraphy    ##
4699 //########################
4700 static void update_presets_list (GObject *tbl)
4702     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4703     if (g_object_get_data(tbl, "presets_blocked"))
4704         return;
4706     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4707     if (!sel) {
4708         // WTF!? This will cause a segfault if ever reached
4709         //ege_select_one_action_set_active(sel, 0);
4710         return;
4711     }
4713     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4715     int ege_index = 1;
4716     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4717         bool match = true;
4719         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4720         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4721             Glib::ustring entry_name = j->getEntryName();
4722             if (entry_name == "id" || entry_name == "name") continue;
4724             void *widget = g_object_get_data(tbl, entry_name.data());
4725             if (widget) {
4726                 if (GTK_IS_ADJUSTMENT(widget)) {
4727                     double v = j->getDouble();
4728                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4729                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4730                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4731                         match = false;
4732                         break;
4733                     }
4734                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4735                     bool v = j->getBool();
4736                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4737                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4738                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4739                         match = false;
4740                         break;
4741                     }
4742                 }
4743             }
4744         }
4746         if (match) {
4747             // newly added item is at the same index as the
4748             // save command, so we need to change twice for it to take effect
4749             ege_select_one_action_set_active(sel, 0);
4750             ege_select_one_action_set_active(sel, ege_index); // one-based index
4751             return;
4752         }
4753     }
4755     // no match found
4756     ege_select_one_action_set_active(sel, 0);
4759 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4761     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4762     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4763     update_presets_list(tbl);
4766 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4768     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4769     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4770     update_presets_list(tbl);
4773 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4775     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4776     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4777     update_presets_list(tbl);
4780 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4782     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4783     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4784     update_presets_list(tbl);
4787 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4789     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4790     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4791     update_presets_list(tbl);
4794 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4796     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4797     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4798     update_presets_list(tbl);
4801 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4803     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4804     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4805     update_presets_list(tbl);
4808 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4810     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4811     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4812     update_presets_list(tbl);
4815 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4817     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4818     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4819     update_presets_list(tbl);
4822 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4824     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4825     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4826     update_presets_list(tbl);
4829 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4831     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4832     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4833     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4834     update_presets_list(tbl);
4835     if (calligraphy_angle )
4836         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4840 static gchar const *const widget_names[] = {
4841     "width",
4842     "mass",
4843     "wiggle",
4844     "angle",
4845     "thinning",
4846     "tremor",
4847     "flatness",
4848     "cap_rounding",
4849     "usepressure",
4850     "tracebackground",
4851     "usetilt"
4852 };
4855 static void sp_dcc_build_presets_list(GObject *tbl)
4857     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4859     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4860     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4861     gtk_list_store_clear (model);
4863     {
4864         GtkTreeIter iter;
4865         gtk_list_store_append( model, &iter );
4866         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4867     }
4869     // iterate over all presets to populate the list
4870     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4871     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4872     int ii=1;
4874     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4875         GtkTreeIter iter;
4876         Glib::ustring preset_name = prefs->getString(*i + "/name");
4877         gtk_list_store_append( model, &iter );
4878         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4879     }
4881     {
4882         GtkTreeIter iter;
4883         gtk_list_store_append( model, &iter );
4884         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4885         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4886     }
4888     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4890     update_presets_list (tbl);
4893 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4895     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4896     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4897     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4898     if (! desktop) return;
4900     if (g_object_get_data(tbl, "presets_blocked"))
4901         return;
4903     CalligraphicProfileRename::show(desktop);
4904     if ( !CalligraphicProfileRename::applied()) {
4905         // dialog cancelled
4906         update_presets_list (tbl);
4907         return;
4908     }
4909     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4911     if (profile_name.empty()) {
4912         // empty name entered
4913         update_presets_list (tbl);
4914         return;
4915     }
4917     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4919     // If there's a preset with the given name, find it and set save_path appropriately
4920     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4921     int total_presets = presets.size();
4922     int new_index = -1;
4923     Glib::ustring save_path; // profile pref path without a trailing slash
4925     int temp_index = 0;
4926     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4927         Glib::ustring name = prefs->getString(*i + "/name");
4928         if (!name.empty() && profile_name == name) {
4929             new_index = temp_index;
4930             save_path = *i;
4931             break;
4932         }
4933     }
4935     if (new_index == -1) {
4936         // no preset with this name, create
4937         new_index = total_presets + 1;
4938         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4939         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4940         g_free(profile_id);
4941     }
4943     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4944         gchar const *const widget_name = widget_names[i];
4945         void *widget = g_object_get_data(tbl, widget_name);
4946         if (widget) {
4947             if (GTK_IS_ADJUSTMENT(widget)) {
4948                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4949                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4950                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4951             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4952                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4953                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4954                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4955             } else {
4956                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4957             }
4958         } else {
4959             g_warning("Bad key when writing preset: %s\n", widget_name);
4960         }
4961     }
4962     prefs->setString(save_path + "/name", profile_name);
4964     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4965     sp_dcc_build_presets_list (tbl);
4969 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4971     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4973     gint preset_index = ege_select_one_action_get_active( act );
4974     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4975     // even when the preset is not changed. It would be good to replace it with something more
4976     // modern. Index 0 means "No preset", so we don't do anything.
4977     if (preset_index == 0) return;
4979     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4981     if (preset_index == save_presets_index) {
4982         // this is the Save command
4983         sp_dcc_save_profile(NULL, tbl);
4984         return;
4985     }
4987     if (g_object_get_data(tbl, "presets_blocked"))
4988         return;
4990     // preset_index is one-based so we subtract 1
4991     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4992     Glib::ustring preset_path = presets.at(preset_index - 1);
4994     if (!preset_path.empty()) {
4995         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
4997         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4999         // Shouldn't this be std::map?
5000         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
5001             Glib::ustring entry_name = i->getEntryName();
5002             if (entry_name == "id" || entry_name == "name") continue;
5003             void *widget = g_object_get_data(tbl, entry_name.data());
5004             if (widget) {
5005                 if (GTK_IS_ADJUSTMENT(widget)) {
5006                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
5007                     gtk_adjustment_set_value(adj, i->getDouble());
5008                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
5009                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
5010                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
5011                     gtk_toggle_action_set_active(toggle, i->getBool());
5012                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
5013                 } else {
5014                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
5015                 }
5016             } else {
5017                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
5018             }
5019         }
5020         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
5021     }
5025 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5027     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5028     {
5029         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
5031         EgeAdjustmentAction* calligraphy_angle = 0;
5033         {
5034         /* Width */
5035         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5036         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5037         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
5038                                                               _("Pen Width"), _("Width:"),
5039                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
5040                                                               "/tools/calligraphic/width", 15,
5041                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
5042                                                               1, 100, 1.0, 10.0,
5043                                                               labels, values, G_N_ELEMENTS(labels),
5044                                                               sp_ddc_width_value_changed,  1, 0 );
5045         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5046         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5047         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5048         }
5050         {
5051         /* Thinning */
5052             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
5053             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
5054         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
5055                                                               _("Stroke Thinning"), _("Thinning:"),
5056                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
5057                                                               "/tools/calligraphic/thinning", 10,
5058                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5059                                                               -100, 100, 1, 10.0,
5060                                                               labels, values, G_N_ELEMENTS(labels),
5061                                                               sp_ddc_velthin_value_changed, 1, 0);
5062         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5063         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5064         }
5066         {
5067         /* Angle */
5068         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5069         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5070         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5071                                                               _("Pen Angle"), _("Angle:"),
5072                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5073                                                               "/tools/calligraphic/angle", 30,
5074                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5075                                                               -90.0, 90.0, 1.0, 10.0,
5076                                                               labels, values, G_N_ELEMENTS(labels),
5077                                                               sp_ddc_angle_value_changed, 1, 0 );
5078         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5079         g_object_set_data( holder, "angle_action", eact );
5080         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5081         calligraphy_angle = eact;
5082         }
5084         {
5085         /* Fixation */
5086             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5087         gdouble values[] = {0, 20, 40, 60, 90, 100};
5088         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5089                                                               _("Fixation"), _("Fixation:"),
5090                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5091                                                               "/tools/calligraphic/flatness", 90,
5092                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5093                                                               0.0, 100, 1.0, 10.0,
5094                                                               labels, values, G_N_ELEMENTS(labels),
5095                                                               sp_ddc_flatness_value_changed, 1, 0);
5096         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5097         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5098         }
5100         {
5101         /* Cap Rounding */
5102             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5103         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5104         // TRANSLATORS: "cap" means "end" (both start and finish) here
5105         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5106                                                               _("Cap rounding"), _("Caps:"),
5107                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5108                                                               "/tools/calligraphic/cap_rounding", 0.0,
5109                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5110                                                               0.0, 5.0, 0.01, 0.1,
5111                                                               labels, values, G_N_ELEMENTS(labels),
5112                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5113         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5114         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5115         }
5117         {
5118         /* Tremor */
5119             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5120         gdouble values[] = {0, 10, 20, 40, 60, 100};
5121         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5122                                                               _("Stroke Tremor"), _("Tremor:"),
5123                                                               _("Increase to make strokes rugged and trembling"),
5124                                                               "/tools/calligraphic/tremor", 0.0,
5125                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5126                                                               0.0, 100, 1, 10.0,
5127                                                               labels, values, G_N_ELEMENTS(labels),
5128                                                               sp_ddc_tremor_value_changed, 1, 0);
5130         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5131         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5132         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5133         }
5135         {
5136         /* Wiggle */
5137         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5138         gdouble values[] = {0, 20, 40, 60, 100};
5139         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5140                                                               _("Pen Wiggle"), _("Wiggle:"),
5141                                                               _("Increase to make the pen waver and wiggle"),
5142                                                               "/tools/calligraphic/wiggle", 0.0,
5143                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5144                                                               0.0, 100, 1, 10.0,
5145                                                               labels, values, G_N_ELEMENTS(labels),
5146                                                               sp_ddc_wiggle_value_changed, 1, 0);
5147         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5148         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5149         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5150         }
5152         {
5153         /* Mass */
5154             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5155         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5156         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5157                                                               _("Pen Mass"), _("Mass:"),
5158                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5159                                                               "/tools/calligraphic/mass", 2.0,
5160                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5161                                                               0.0, 100, 1, 10.0,
5162                                                               labels, values, G_N_ELEMENTS(labels),
5163                                                               sp_ddc_mass_value_changed, 1, 0);
5164         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5165         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5166         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5167         }
5170         /* Trace Background button */
5171         {
5172             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5173                                                           _("Trace Background"),
5174                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5175                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5176                                                           Inkscape::ICON_SIZE_DECORATION );
5177             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5178             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5179             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5180             g_object_set_data( holder, "tracebackground", act );
5181         }
5183         /* Use Pressure button */
5184         {
5185             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5186                                                           _("Pressure"),
5187                                                           _("Use the pressure of the input device to alter the width of the pen"),
5188                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5189                                                           Inkscape::ICON_SIZE_DECORATION );
5190             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5191             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5192             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5193             g_object_set_data( holder, "usepressure", act );
5194         }
5196         /* Use Tilt button */
5197         {
5198             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5199                                                           _("Tilt"),
5200                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5201                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5202                                                           Inkscape::ICON_SIZE_DECORATION );
5203             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5204             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5205             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5206             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5207             g_object_set_data( holder, "usetilt", act );
5208         }
5210         /*calligraphic profile */
5211         {
5212             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5213             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5214             ege_select_one_action_set_appearance (act1, "compact");
5215             g_object_set_data (holder, "profile_selector", act1 );
5217             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5219             sp_dcc_build_presets_list (holder);
5221             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5222             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5223         }
5224     }
5228 //########################
5229 //##    Circle / Arc    ##
5230 //########################
5232 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5234     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5235     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5237     if (v1 == 0 && v2 == 0) {
5238         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5239             gtk_action_set_sensitive( ocb, FALSE );
5240             gtk_action_set_sensitive( make_whole, FALSE );
5241         }
5242     } else {
5243         gtk_action_set_sensitive( ocb, TRUE );
5244         gtk_action_set_sensitive( make_whole, TRUE );
5245     }
5248 static void
5249 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5251     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5253     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5254         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5255         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5256     }
5258     // quit if run by the attr_changed listener
5259     if (g_object_get_data( tbl, "freeze" )) {
5260         return;
5261     }
5263     // in turn, prevent listener from responding
5264     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5266     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5268     bool modmade = false;
5269     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5270          items != NULL;
5271          items = items->next)
5272     {
5273         SPItem *item = SP_ITEM(items->data);
5275         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5277             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5278             SPArc *arc = SP_ARC(item);
5280             if (!strcmp(value_name, "start"))
5281                 ge->start = (adj->value * M_PI)/ 180;
5282             else
5283                 ge->end = (adj->value * M_PI)/ 180;
5285             sp_genericellipse_normalize(ge);
5286             ((SPObject *)arc)->updateRepr();
5287             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5289             modmade = true;
5290         }
5291     }
5293     g_free(namespaced_name);
5295     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5297     sp_arctb_sensitivize( tbl, adj->value, other->value );
5299     if (modmade) {
5300         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5301                                    _("Arc: Change start/end"));
5302     }
5304     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5308 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5310     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5313 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5315     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5319 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5321     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5322     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5323         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5324         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5325     }
5327     // quit if run by the attr_changed listener
5328     if (g_object_get_data( tbl, "freeze" )) {
5329         return;
5330     }
5332     // in turn, prevent listener from responding
5333     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5335     bool modmade = false;
5337     if ( ege_select_one_action_get_active(act) != 0 ) {
5338         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5339              items != NULL;
5340              items = items->next)
5341         {
5342             if (SP_IS_ARC((SPItem *) items->data)) {
5343                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5344                 repr->setAttribute("sodipodi:open", "true");
5345                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5346                 modmade = true;
5347             }
5348         }
5349     } else {
5350         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5351              items != NULL;
5352              items = items->next)
5353         {
5354             if (SP_IS_ARC((SPItem *) items->data))    {
5355                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5356                 repr->setAttribute("sodipodi:open", NULL);
5357                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5358                 modmade = true;
5359             }
5360         }
5361     }
5363     if (modmade) {
5364         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5365                                    _("Arc: Change open/closed"));
5366     }
5368     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5371 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5373     GtkAdjustment *adj;
5374     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5375     gtk_adjustment_set_value(adj, 0.0);
5376     gtk_adjustment_value_changed(adj);
5378     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5379     gtk_adjustment_set_value(adj, 0.0);
5380     gtk_adjustment_value_changed(adj);
5382     spinbutton_defocus( GTK_OBJECT(obj) );
5385 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5386                                       gchar const */*old_value*/, gchar const */*new_value*/,
5387                                       bool /*is_interactive*/, gpointer data)
5389     GObject *tbl = G_OBJECT(data);
5391     // quit if run by the _changed callbacks
5392     if (g_object_get_data( tbl, "freeze" )) {
5393         return;
5394     }
5396     // in turn, prevent callbacks from responding
5397     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5399     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5400     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5402     GtkAdjustment *adj1,*adj2;
5403     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5404     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5405     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5406     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5408     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5410     char const *openstr = NULL;
5411     openstr = repr->attribute("sodipodi:open");
5412     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5414     if (openstr) {
5415         ege_select_one_action_set_active( ocb, 1 );
5416     } else {
5417         ege_select_one_action_set_active( ocb, 0 );
5418     }
5420     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5423 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5424     NULL, /* child_added */
5425     NULL, /* child_removed */
5426     arc_tb_event_attr_changed,
5427     NULL, /* content_changed */
5428     NULL  /* order_changed */
5429 };
5432 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5434     int n_selected = 0;
5435     Inkscape::XML::Node *repr = NULL;
5437     purge_repr_listener( tbl, tbl );
5439     for (GSList const *items = selection->itemList();
5440          items != NULL;
5441          items = items->next)
5442     {
5443         if (SP_IS_ARC((SPItem *) items->data)) {
5444             n_selected++;
5445             repr = SP_OBJECT_REPR((SPItem *) items->data);
5446         }
5447     }
5449     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5451     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5452     if (n_selected == 0) {
5453         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5454     } else if (n_selected == 1) {
5455         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5456         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5458         if (repr) {
5459             g_object_set_data( tbl, "repr", repr );
5460             Inkscape::GC::anchor(repr);
5461             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5462             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5463         }
5464     } else {
5465         // FIXME: implement averaging of all parameters for multiple selected
5466         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5467         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5468         sp_arctb_sensitivize( tbl, 1, 0 );
5469     }
5473 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5475     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5477     EgeAdjustmentAction* eact = 0;
5478     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5481     {
5482         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5483         ege_output_action_set_use_markup( act, TRUE );
5484         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5485         g_object_set_data( holder, "mode_action", act );
5486     }
5488     /* Start */
5489     {
5490         eact = create_adjustment_action( "ArcStartAction",
5491                                          _("Start"), _("Start:"),
5492                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5493                                          "/tools/shapes/arc/start", 0.0,
5494                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5495                                          -360.0, 360.0, 1.0, 10.0,
5496                                          0, 0, 0,
5497                                          sp_arctb_start_value_changed);
5498         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5499     }
5501     /* End */
5502     {
5503         eact = create_adjustment_action( "ArcEndAction",
5504                                          _("End"), _("End:"),
5505                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5506                                          "/tools/shapes/arc/end", 0.0,
5507                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5508                                          -360.0, 360.0, 1.0, 10.0,
5509                                          0, 0, 0,
5510                                          sp_arctb_end_value_changed);
5511         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5512     }
5514     /* Segments / Pie checkbox */
5515     {
5516         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5518         GtkTreeIter iter;
5519         gtk_list_store_append( model, &iter );
5520         gtk_list_store_set( model, &iter,
5521                             0, _("Closed arc"),
5522                             1, _("Switch to segment (closed shape with two radii)"),
5523                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5524                             -1 );
5526         gtk_list_store_append( model, &iter );
5527         gtk_list_store_set( model, &iter,
5528                             0, _("Open Arc"),
5529                             1, _("Switch to arc (unclosed shape)"),
5530                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5531                             -1 );
5533         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5534         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5535         g_object_set_data( holder, "open_action", act );
5537         ege_select_one_action_set_appearance( act, "full" );
5538         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5539         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5540         ege_select_one_action_set_icon_column( act, 2 );
5541         ege_select_one_action_set_icon_size( act, secondarySize );
5542         ege_select_one_action_set_tooltip_column( act, 1  );
5544         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5545         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5546         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5547     }
5549     /* Make Whole */
5550     {
5551         InkAction* inky = ink_action_new( "ArcResetAction",
5552                                           _("Make whole"),
5553                                           _("Make the shape a whole ellipse, not arc or segment"),
5554                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5555                                           secondarySize );
5556         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5557         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5558         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5559         g_object_set_data( holder, "make_whole", inky );
5560     }
5562     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5563     // sensitivize make whole and open checkbox
5564     {
5565         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5566         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5567         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5568     }
5571     sigc::connection *connection = new sigc::connection(
5572         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5573         );
5574     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5575     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5581 // toggle button callbacks and updaters
5583 //########################
5584 //##      Dropper       ##
5585 //########################
5587 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5588     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5589     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5590     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5591     if ( set_action ) {
5592         if ( gtk_toggle_action_get_active( act ) ) {
5593             gtk_action_set_sensitive( set_action, TRUE );
5594         } else {
5595             gtk_action_set_sensitive( set_action, FALSE );
5596         }
5597     }
5599     spinbutton_defocus(GTK_OBJECT(tbl));
5602 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5603     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5604     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5605     spinbutton_defocus(GTK_OBJECT(tbl));
5609 /**
5610  * Dropper auxiliary toolbar construction and setup.
5611  *
5612  * TODO: Would like to add swatch of current color.
5613  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5614  *       can drag and drop places. Will provide a nice mixing palette.
5615  */
5616 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5618     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5619     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5621     {
5622         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5623         ege_output_action_set_use_markup( act, TRUE );
5624         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5625     }
5627     {
5628         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5629                                                       _("Pick opacity"),
5630                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5631                                                       NULL,
5632                                                       Inkscape::ICON_SIZE_DECORATION );
5633         g_object_set( act, "short_label", _("Pick"), NULL );
5634         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5635         g_object_set_data( holder, "pick_action", act );
5636         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5637         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5638     }
5640     {
5641         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5642                                                       _("Assign opacity"),
5643                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5644                                                       NULL,
5645                                                       Inkscape::ICON_SIZE_DECORATION );
5646         g_object_set( act, "short_label", _("Assign"), NULL );
5647         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5648         g_object_set_data( holder, "set_action", act );
5649         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5650         // make sure it's disabled if we're not picking alpha
5651         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5652         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5653     }
5657 //########################
5658 //##      LPETool       ##
5659 //########################
5661 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5663 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5664 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5666     using namespace Inkscape::LivePathEffect;
5668     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5669     SPEventContext *ec = desktop->event_context;
5670     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5671         return;
5672     }
5674     // only take action if run by the attr_changed listener
5675     if (!g_object_get_data(tbl, "freeze")) {
5676         // in turn, prevent listener from responding
5677         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5679         gint mode = ege_select_one_action_get_active(act);
5680         EffectType type = lpesubtools[mode].type;
5682         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5683         bool success = lpetool_try_construction(lc, type);
5684         if (success) {
5685             // since the construction was already performed, we set the state back to inactive
5686             ege_select_one_action_set_active(act, 0);
5687             mode = 0;
5688         } else {
5689             // switch to the chosen subtool
5690             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5691         }
5693         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5694             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5695             prefs->setInt( "/tools/lpetool/mode", mode );
5696         }
5698         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5699     }
5702 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5704     SPEventContext *ec = selection->desktop()->event_context;
5705     if (!SP_IS_LPETOOL_CONTEXT(ec))
5706         return;
5708     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5711 void
5712 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5714     using namespace Inkscape::LivePathEffect;
5715     SPEventContext *ec = selection->desktop()->event_context;
5716     if (!SP_IS_LPETOOL_CONTEXT(ec))
5717         return;
5718     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5720     lpetool_delete_measuring_items(lc);
5721     lpetool_create_measuring_items(lc, selection);
5723     // activate line segment combo box if a single item with LPELineSegment is selected
5724     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5725     SPItem *item = selection->singleItem();
5726     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5727         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5728         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5729         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5730             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5731             g_object_set_data(tbl, "currentlpe", lpe);
5732             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5733             gtk_action_set_sensitive(w, TRUE);
5734             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5735         } else {
5736             g_object_set_data(tbl, "currentlpe", NULL);
5737             g_object_set_data(tbl, "currentlpeitem", NULL);
5738             gtk_action_set_sensitive(w, FALSE);
5739         }
5740     } else {
5741         g_object_set_data(tbl, "currentlpe", NULL);
5742         g_object_set_data(tbl, "currentlpeitem", NULL);
5743         gtk_action_set_sensitive(w, FALSE);
5744     }
5747 static void
5748 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5749     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5750     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5752     bool show = gtk_toggle_action_get_active( act );
5753     prefs->setBool("/tools/lpetool/show_bbox",  show);
5755     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5756         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5757         lpetool_context_reset_limiting_bbox(lc);
5758     }
5761 static void
5762 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5763     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5764     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5765         return;
5767     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5768     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5769     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5770         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5771         bool show = gtk_toggle_action_get_active( act );
5772         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5773         lpetool_show_measuring_info(lc, show);
5774         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5775     }
5778 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5779     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5780     SPUnit const *unit = tracker->getActiveUnit();
5781     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5782     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5784     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5785     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5786         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5787         lpetool_delete_measuring_items(lc);
5788         lpetool_create_measuring_items(lc);
5789     }
5792 static void
5793 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5794     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5795     Inkscape::Selection *selection = desktop->selection;
5797     Geom::OptRect bbox = selection->bounds();
5799     if (bbox) {
5800         Geom::Point A(bbox->min());
5801         Geom::Point B(bbox->max());
5803         A *= desktop->doc2dt();
5804         B *= desktop->doc2dt();
5806         // TODO: should we provide a way to store points in prefs?
5807         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5808         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5809         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5810         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5811         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5813         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5814     }
5816     gtk_toggle_action_set_active(act, false);
5819 static void
5820 sp_line_segment_build_list(GObject *tbl)
5822     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5824     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5825     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5826     gtk_list_store_clear (model);
5828     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5829     {
5830         GtkTreeIter iter;
5831         gtk_list_store_append( model, &iter );
5832         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5833         gtk_list_store_append( model, &iter );
5834         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5835         gtk_list_store_append( model, &iter );
5836         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5837         gtk_list_store_append( model, &iter );
5838         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5839     }
5841     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5844 static void
5845 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5846     using namespace Inkscape::LivePathEffect;
5848     // quit if run by the attr_changed listener
5849     if (g_object_get_data(tbl, "freeze")) {
5850         return;
5851     }
5853     // in turn, prevent listener from responding
5854     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5856     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5857     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5858     if (lpeitem) {
5859         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5860         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5861         sp_lpe_item_update_patheffect(lpeitem, true, true);
5862     }
5864     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5867 static void
5868 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5869     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5871     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5872         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5873     }
5874     gtk_toggle_action_set_active(act, false);
5877 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5879     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5880     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5881     g_object_set_data(holder, "tracker", tracker);
5882     SPUnit const *unit = tracker->getActiveUnit();
5884     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5885     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5887     /** Automatically create a list of LPEs that get added to the toolbar **/
5888     {
5889         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5891         GtkTreeIter iter;
5893         // the first toggle button represents the state that no subtool is active (remove this when
5894         // this can be modeled by EgeSelectOneAction or some other action)
5895         gtk_list_store_append( model, &iter );
5896         gtk_list_store_set( model, &iter,
5897                             0, _("All inactive"),
5898                             1, _("No geometric tool is active"),
5899                             2, "draw-geometry-inactive",
5900                             -1 );
5902         Inkscape::LivePathEffect::EffectType type;
5903         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5904             type =  lpesubtools[i].type;
5905             gtk_list_store_append( model, &iter );
5906             gtk_list_store_set( model, &iter,
5907                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5908                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5909                                 2, lpesubtools[i].icon_name,
5910                                 -1 );
5911         }
5913         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5914         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5915         g_object_set_data( holder, "lpetool_mode_action", act );
5917         ege_select_one_action_set_appearance( act, "full" );
5918         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5919         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5920         ege_select_one_action_set_icon_column( act, 2 );
5921         ege_select_one_action_set_tooltip_column( act, 1  );
5923         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5924         ege_select_one_action_set_active( act, lpeToolMode );
5925         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5926     }
5928     /* Show limiting bounding box */
5929     {
5930         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5931                                                       _("Show limiting bounding box"),
5932                                                       _("Show bounding box (used to cut infinite lines)"),
5933                                                       "show-bounding-box",
5934                                                       Inkscape::ICON_SIZE_DECORATION );
5935         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5936         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5937         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5938     }
5940     /* Set limiting bounding box to bbox of current selection */
5941     {
5942         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5943                                                       _("Get limiting bounding box from selection"),
5944                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5945                                                       "draw-geometry-set-bounding-box",
5946                                                       Inkscape::ICON_SIZE_DECORATION );
5947         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5948         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5949         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5950     }
5953     /* Combo box to choose line segment type */
5954     {
5955         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5956         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5957         ege_select_one_action_set_appearance (act, "compact");
5958         g_object_set_data (holder, "lpetool_line_segment_action", act );
5960         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5962         sp_line_segment_build_list (holder);
5964         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5965         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5966         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5967     }
5969     /* Display measuring info for selected items */
5970     {
5971         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5972                                                       _("Display measuring info"),
5973                                                       _("Display measuring info for selected items"),
5974                                                       "draw-geometry-show-measuring-info",
5975                                                       Inkscape::ICON_SIZE_DECORATION );
5976         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5977         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5978         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5979     }
5981     // add the units menu
5982     {
5983         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5984         gtk_action_group_add_action( mainActions, act );
5985         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5986         g_object_set_data(holder, "lpetool_units_action", act);
5987         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5988     }
5990     /* Open LPE dialog (to adapt parameters numerically) */
5991     {
5992         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5993                                                       _("Open LPE dialog"),
5994                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5995                                                       "dialog-geometry",
5996                                                       Inkscape::ICON_SIZE_DECORATION );
5997         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5998         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5999         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
6000     }
6002     //watch selection
6003     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
6005     sigc::connection *c_selection_modified =
6006         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6007                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
6008     pool->add_connection ("selection-modified", c_selection_modified);
6010     sigc::connection *c_selection_changed =
6011         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6012                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
6013     pool->add_connection ("selection-changed", c_selection_changed);
6016 //########################
6017 //##       Eraser       ##
6018 //########################
6020 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
6022     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6023     prefs->setDouble( "/tools/eraser/width", adj->value );
6024     update_presets_list(tbl);
6027 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6029     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6030     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
6031     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
6032         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6033         prefs->setBool( "/tools/eraser/mode", eraserMode );
6034     }
6036     // only take action if run by the attr_changed listener
6037     if (!g_object_get_data( tbl, "freeze" )) {
6038         // in turn, prevent listener from responding
6039         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6041         if ( eraserMode != 0 ) {
6042         } else {
6043         }
6044         // TODO finish implementation
6046         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6047     }
6050 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6052     {
6053         /* Width */
6054         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
6055         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
6056         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
6057                                                               _("Pen Width"), _("Width:"),
6058                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
6059                                                               "/tools/eraser/width", 15,
6060                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
6061                                                               1, 100, 1.0, 10.0,
6062                                                               labels, values, G_N_ELEMENTS(labels),
6063                                                               sp_erc_width_value_changed, 1, 0);
6064         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6065         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6066         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6067     }
6069     {
6070         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6072         GtkTreeIter iter;
6073         gtk_list_store_append( model, &iter );
6074         gtk_list_store_set( model, &iter,
6075                             0, _("Delete"),
6076                             1, _("Delete objects touched by the eraser"),
6077                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6078                             -1 );
6080         gtk_list_store_append( model, &iter );
6081         gtk_list_store_set( model, &iter,
6082                             0, _("Cut"),
6083                             1, _("Cut out from objects"),
6084                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6085                             -1 );
6087         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6088         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6089         g_object_set_data( holder, "eraser_mode_action", act );
6091         ege_select_one_action_set_appearance( act, "full" );
6092         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6093         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6094         ege_select_one_action_set_icon_column( act, 2 );
6095         ege_select_one_action_set_tooltip_column( act, 1  );
6097         /// @todo Convert to boolean?
6098         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6099         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6100         ege_select_one_action_set_active( act, eraserMode );
6101         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6102     }
6106 //########################
6107 //##    Text Toolbox    ##
6108 //########################
6109 /*
6110 static void
6111 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6113     //Call back for letter sizing spinbutton
6116 static void
6117 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6119     //Call back for line height spinbutton
6122 static void
6123 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6125     //Call back for horizontal kerning spinbutton
6128 static void
6129 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6131     //Call back for vertical kerning spinbutton
6134 static void
6135 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6137     //Call back for letter rotation spinbutton
6138 }*/
6140 namespace {
6142 void
6143 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6145     // quit if run by the _changed callbacks
6146     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6147         return;
6148     }
6150     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6152     SPStyle *query =
6153         sp_style_new (SP_ACTIVE_DOCUMENT);
6155     int result_family =
6156         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6158     int result_style =
6159         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6161     int result_numbers =
6162         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6164     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6166     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6167     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6168         // there are no texts in selection, read from prefs
6170         sp_style_read_from_prefs(query, "/tools/text");
6172         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6173             // do not reset the toolbar style from prefs if we already did it last time
6174             sp_style_unref(query);
6175             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6176             return;
6177         }
6178         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6179     } else {
6180         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6181     }
6183     if (query->text)
6184     {
6185         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6186             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6187             gtk_entry_set_text (GTK_ENTRY (entry), "");
6189         } else if (query->text->font_specification.value || query->text->font_family.value) {
6191             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6192             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6194             // Get the font that corresponds
6195             Glib::ustring familyName;
6197             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6198             if (font) {
6199                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6200                 font->Unref();
6201                 font = NULL;
6202             }
6204             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6206             Gtk::TreeIter iter;
6207             try {
6208                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6209                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6210                 iter = model->get_iter(path);
6211             } catch (...) {
6212                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6213                 sp_style_unref(query);
6214                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6215                 return;
6216             }
6218             combo->set_active (iter);
6219         }
6221         //Size
6222         {
6223             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6224             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6225             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6226             g_free(str);
6227         }
6229         //Anchor
6230         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6231         {
6232             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6233             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6234             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6235             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6236         }
6237         else
6238         {
6239             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6240             {
6241                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6242                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6243                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6244                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6245             }
6246             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6247             {
6248                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6249                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6250                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6251                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6252             }
6253             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6254             {
6255                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6256                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6257                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6258                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6259             }
6260         }
6262         //Style
6263         {
6264             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6266             gboolean active = gtk_toggle_button_get_active (button);
6267             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));
6269             if (active != check)
6270             {
6271                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6272                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6273                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6274             }
6275         }
6277         {
6278             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6280             gboolean active = gtk_toggle_button_get_active (button);
6281             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6283             if (active != check)
6284             {
6285                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6286                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6287                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6288             }
6289         }
6291         //Orientation
6292         //locking both buttons, changing one affect all group (both)
6293         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6294         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6296         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6297         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6299         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6300         {
6301             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6302         }
6303         else
6304         {
6305             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6306         }
6307         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6308         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6309     }
6311     sp_style_unref(query);
6313     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6316 void
6317 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6319     sp_text_toolbox_selection_changed (selection, tbl);
6322 void
6323 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6325     sp_text_toolbox_selection_changed (NULL, tbl);
6328 void
6329 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6330                                 GObject             *tbl)
6332     // quit if run by the _changed callbacks
6333     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6334         return;
6335     }
6337     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6339     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6340     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6341     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6343     //g_print ("family changed to: %s\n", family);
6345     SPStyle *query =
6346         sp_style_new (SP_ACTIVE_DOCUMENT);
6348     int result_fontspec =
6349         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6351     SPCSSAttr *css = sp_repr_css_attr_new ();
6353     // First try to get the font spec from the stored value
6354     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6356     if (fontSpec.empty()) {
6357         // Construct a new font specification if it does not yet exist
6358         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6359         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6360         fontFromStyle->Unref();
6361     }
6363     if (!fontSpec.empty()) {
6365         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6367         if (!newFontSpec.empty()) {
6369             if (fontSpec != newFontSpec) {
6371                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6373                 if (font) {
6374                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6376                     // Set all the these just in case they were altered when finding the best
6377                     // match for the new family and old style...
6379                     gchar c[256];
6381                     font->Family(c, 256);
6383                     sp_repr_css_set_property (css, "font-family", c);
6385                     font->Attribute( "weight", c, 256);
6386                     sp_repr_css_set_property (css, "font-weight", c);
6388                     font->Attribute("style", c, 256);
6389                     sp_repr_css_set_property (css, "font-style", c);
6391                     font->Attribute("stretch", c, 256);
6392                     sp_repr_css_set_property (css, "font-stretch", c);
6394                     font->Attribute("variant", c, 256);
6395                     sp_repr_css_set_property (css, "font-variant", c);
6397                     font->Unref();
6398                 }
6399             }
6401         } else {
6402             // If the old font on selection (or default) was not existing on the system,
6403             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6404             // setting the family reported by the family chooser.
6406             //g_print ("fallback setting family: %s\n", family);
6407             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6408             sp_repr_css_set_property (css, "font-family", family);
6409         }
6410     }
6412     // If querying returned nothing, set the default style of the tool (for new texts)
6413     if (result_fontspec == QUERY_STYLE_NOTHING)
6414     {
6415         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6416         prefs->mergeStyle("/tools/text/style", css);
6417         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6418     }
6419     else
6420     {
6421         sp_desktop_set_style (desktop, css, true, true);
6422     }
6424     sp_style_unref(query);
6426     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6427                                    _("Text: Change font family"));
6428     sp_repr_css_attr_unref (css);
6430     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6432     // unfreeze
6433     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6435     // focus to canvas
6436     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6440 void
6441 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6442                                    gpointer          data)
6444     if (g_object_get_data (G_OBJECT (button), "block")) return;
6445     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6446     int prop = GPOINTER_TO_INT(data);
6448     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6450     // move the x of all texts to preserve the same bbox
6451     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6452     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6453         if (SP_IS_TEXT((SPItem *) items->data)) {
6454             SPItem *item = SP_ITEM(items->data);
6456             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6457             // below, variable names suggest horizontal move, but we check the writing direction
6458             // and move in the corresponding axis
6459             int axis;
6460             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6461                 axis = NR::X;
6462             } else {
6463                 axis = NR::Y;
6464             }
6466             Geom::OptRect bbox
6467                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6468             if (!bbox)
6469                 continue;
6470             double width = bbox->dimensions()[axis];
6471             // If you want to align within some frame, other than the text's own bbox, calculate
6472             // the left and right (or top and bottom for tb text) slacks of the text inside that
6473             // frame (currently unused)
6474             double left_slack = 0;
6475             double right_slack = 0;
6476             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6477             double move = 0;
6478             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6479                 switch (prop) {
6480                     case 0:
6481                         move = -left_slack;
6482                         break;
6483                     case 1:
6484                         move = width/2 + (right_slack - left_slack)/2;
6485                         break;
6486                     case 2:
6487                         move = width + right_slack;
6488                         break;
6489                 }
6490             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6491                 switch (prop) {
6492                     case 0:
6493                         move = -width/2 - left_slack;
6494                         break;
6495                     case 1:
6496                         move = (right_slack - left_slack)/2;
6497                         break;
6498                     case 2:
6499                         move = width/2 + right_slack;
6500                         break;
6501                 }
6502             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6503                 switch (prop) {
6504                     case 0:
6505                         move = -width - left_slack;
6506                         break;
6507                     case 1:
6508                         move = -width/2 + (right_slack - left_slack)/2;
6509                         break;
6510                     case 2:
6511                         move = right_slack;
6512                         break;
6513                 }
6514             }
6515             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6516             if (axis == NR::X) {
6517                 XY = XY + Geom::Point (move, 0);
6518             } else {
6519                 XY = XY + Geom::Point (0, move);
6520             }
6521             SP_TEXT(item)->attributes.setFirstXY(XY);
6522             SP_OBJECT(item)->updateRepr();
6523             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6524         }
6525     }
6527     SPCSSAttr *css = sp_repr_css_attr_new ();
6528     switch (prop)
6529     {
6530         case 0:
6531         {
6532             sp_repr_css_set_property (css, "text-anchor", "start");
6533             sp_repr_css_set_property (css, "text-align", "start");
6534             break;
6535         }
6536         case 1:
6537         {
6538             sp_repr_css_set_property (css, "text-anchor", "middle");
6539             sp_repr_css_set_property (css, "text-align", "center");
6540             break;
6541         }
6543         case 2:
6544         {
6545             sp_repr_css_set_property (css, "text-anchor", "end");
6546             sp_repr_css_set_property (css, "text-align", "end");
6547             break;
6548         }
6550         case 3:
6551         {
6552             sp_repr_css_set_property (css, "text-anchor", "start");
6553             sp_repr_css_set_property (css, "text-align", "justify");
6554             break;
6555         }
6556     }
6558     SPStyle *query =
6559         sp_style_new (SP_ACTIVE_DOCUMENT);
6560     int result_numbers =
6561         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6563     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6564     if (result_numbers == QUERY_STYLE_NOTHING)
6565     {
6566         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6567         prefs->mergeStyle("/tools/text/style", css);
6568     }
6570     sp_style_unref(query);
6572     sp_desktop_set_style (desktop, css, true, true);
6573     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6574                                    _("Text: Change alignment"));
6575     sp_repr_css_attr_unref (css);
6577     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6580 void
6581 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6582                                gpointer          data)
6584     if (g_object_get_data (G_OBJECT (button), "block")) return;
6586     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6587     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6588     int          prop       = GPOINTER_TO_INT(data);
6589     bool         active     = gtk_toggle_button_get_active (button);
6591     SPStyle *query =
6592         sp_style_new (SP_ACTIVE_DOCUMENT);
6594     int result_fontspec =
6595         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6597     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6598     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6599     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6601     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6602     Glib::ustring newFontSpec = "";
6604     if (fontSpec.empty()) {
6605         // Construct a new font specification if it does not yet exist
6606         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6607         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6608         fontFromStyle->Unref();
6609     }
6611     bool nochange = true;
6612     switch (prop)
6613     {
6614         case 0:
6615         {
6616             if (!fontSpec.empty()) {
6617                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6618                 if (!newFontSpec.empty()) {
6619                     // Don't even set the bold if the font didn't exist on the system
6620                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6621                     nochange = false;
6622                 }
6623             }
6624             // set or reset the button according
6625             if(nochange) {
6626                 gboolean check = gtk_toggle_button_get_active (button);
6628                 if (active != check)
6629                 {
6630                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6631                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6632                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6633                 }
6634             }
6636             break;
6637         }
6639         case 1:
6640         {
6641             if (!fontSpec.empty()) {
6642                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6643                 if (!newFontSpec.empty()) {
6644                     // Don't even set the italic if the font didn't exist on the system
6645                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6646                     nochange = false;
6647                 }
6648             }
6649             if(nochange) {
6650                 gboolean check = gtk_toggle_button_get_active (button);
6652                 if (active != check)
6653                 {
6654                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6655                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6656                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6657                 }
6658             }
6659             break;
6660         }
6661     }
6663     if (!newFontSpec.empty()) {
6664         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6665     }
6667     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6668     if (result_fontspec == QUERY_STYLE_NOTHING)
6669     {
6670         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6671         prefs->mergeStyle("/tools/text/style", css);
6672     }
6674     sp_style_unref(query);
6676     sp_desktop_set_style (desktop, css, true, true);
6677     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6678                                    _("Text: Change font style"));
6679     sp_repr_css_attr_unref (css);
6681     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6684 void
6685 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6686                                      gpointer         data)
6688     if (g_object_get_data (G_OBJECT (button), "block")) {
6689         return;
6690     }
6692     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6693     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6694     int          prop       = GPOINTER_TO_INT(data);
6696     switch (prop)
6697     {
6698         case 0:
6699         {
6700             sp_repr_css_set_property (css, "writing-mode", "lr");
6701             break;
6702         }
6704         case 1:
6705         {
6706             sp_repr_css_set_property (css, "writing-mode", "tb");
6707             break;
6708         }
6709     }
6711     SPStyle *query =
6712         sp_style_new (SP_ACTIVE_DOCUMENT);
6713     int result_numbers =
6714         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6716     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6717     if (result_numbers == QUERY_STYLE_NOTHING)
6718     {
6719         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6720         prefs->mergeStyle("/tools/text/style", css);
6721     }
6723     sp_desktop_set_style (desktop, css, true, true);
6724     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6725                                    _("Text: Change orientation"));
6726     sp_repr_css_attr_unref (css);
6728     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6731 gboolean
6732 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6734     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6735     if (!desktop) return FALSE;
6737     switch (get_group0_keyval (event)) {
6738         case GDK_KP_Enter: // chosen
6739         case GDK_Return:
6740             // unfreeze and update, which will defocus
6741             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6742             sp_text_toolbox_family_changed (NULL, tbl); 
6743             return TRUE; // I consumed the event
6744             break;
6745         case GDK_Escape: 
6746             // defocus
6747             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6748             return TRUE; // I consumed the event
6749             break;
6750     }
6751     return FALSE;
6754 gboolean
6755 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6757     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6758     if (!desktop) return FALSE;
6760     switch (get_group0_keyval (event)) {
6761         case GDK_KP_Enter:
6762         case GDK_Return:
6763         case GDK_Escape: // defocus
6764             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6765             return TRUE; // I consumed the event
6766             break;
6767         case GDK_w:
6768         case GDK_W:
6769             if (event->state & GDK_CONTROL_MASK) {
6770                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6771                 return TRUE; // I consumed the event
6772             }
6773             break;
6774     }
6775     return FALSE;
6779 void
6780 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6781                                GObject     *tbl)
6783      // quit if run by the _changed callbacks
6784     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6785         return;
6786     }
6788     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6790    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6792     // If this is not from selecting a size in the list (in which case get_active will give the
6793     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6794     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6795     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6796    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6797         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6798         return;
6799    }
6801     gdouble value = -1;
6802     {
6803         gchar *endptr;
6804         gchar *const text = gtk_combo_box_get_active_text(cbox);
6805         if (text) {
6806             value = g_strtod(text, &endptr);
6807             if (endptr == text) {  // Conversion failed, non-numeric input.
6808                 value = -1;
6809             }
6810             g_free(text);
6811         }
6812     }
6813     if (value <= 0) {
6814         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6815         return; // could not parse value
6816     }
6818     SPCSSAttr *css = sp_repr_css_attr_new ();
6819     Inkscape::CSSOStringStream osfs;
6820     osfs << value;
6821     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6823     SPStyle *query =
6824         sp_style_new (SP_ACTIVE_DOCUMENT);
6825     int result_numbers =
6826         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6828     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6829     if (result_numbers == QUERY_STYLE_NOTHING)
6830     {
6831         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6832         prefs->mergeStyle("/tools/text/style", css);
6833     }
6835     sp_style_unref(query);
6837     sp_desktop_set_style (desktop, css, true, true);
6838     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6839                                    _("Text: Change font size"));
6840     sp_repr_css_attr_unref (css);
6842     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6844     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6847 gboolean
6848 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6850     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6851     if (!desktop) return FALSE;
6853     if (!g_object_get_data (tbl, "esc-pressed")) {
6854         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6855         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6856         sp_text_toolbox_size_changed (cbox, tbl);
6857         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6858     }
6859     return FALSE; // I consumed the event
6863 gboolean
6864 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6866     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6867     if (!desktop) return FALSE;
6869     switch (get_group0_keyval (event)) {
6870         case GDK_Escape: // defocus
6871             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6872             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6873             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6874             return TRUE; // I consumed the event
6875             break;
6876         case GDK_Return: // defocus
6877         case GDK_KP_Enter:
6878             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6879             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6880             sp_text_toolbox_size_changed (cbox, tbl);
6881             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6882             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6883             return TRUE; // I consumed the event
6884             break;
6885     }
6886     return FALSE;
6889 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6890 // does not work!
6891 gboolean
6892 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6893                                  GdkEventFocus    */*event*/,
6894                                  GObject          *tbl)
6896     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6897     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6898     return FALSE;
6901 gboolean
6902 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6903                                  GdkEventFocus    */*event*/,
6904                                  GObject          *tbl)
6906     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6907     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6908     return FALSE;
6911 void
6912 cell_data_func  (GtkCellLayout */*cell_layout*/,
6913                  GtkCellRenderer   *cell,
6914                  GtkTreeModel      *tree_model,
6915                  GtkTreeIter       *iter,
6916                  gpointer           /*data*/)
6918     gchar *family;
6919     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6920     gchar *const family_escaped = g_markup_escape_text(family, -1);
6922     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6923     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6924     if (show_sample) {
6926         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6927         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6929     std::stringstream markup;
6930     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6931            << family_escaped << "'>" << sample_escaped << "</span>";
6932     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6934         g_free(sample_escaped);
6935     } else {
6936         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6937     }
6939     g_free(family);
6940     g_free(family_escaped);
6943 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6944                                                 GtkTreeModel       *model,
6945                                                 GtkTreeIter        *iter,
6946                                                 GObject            *tbl)
6948     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6949     // press Enter again after choosing a completion)
6950     gchar *family = 0;
6951     gtk_tree_model_get(model, iter, 0, &family, -1);
6953     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6954     gtk_entry_set_text (GTK_ENTRY (entry), family);
6956     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6957     sp_text_toolbox_family_changed (NULL, tbl);
6958     return TRUE;
6962 static void
6963 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6964     GtkEntry *entry;
6965     GtkEntryCompletion *completion;
6966     GtkTreeModel *model;
6968     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6969     completion = gtk_entry_completion_new();
6970     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6971     gtk_entry_completion_set_model(completion, model);
6972     gtk_entry_completion_set_text_column(completion, 0);
6973     gtk_entry_completion_set_inline_completion(completion, FALSE);
6974     gtk_entry_completion_set_inline_selection(completion, FALSE);
6975     gtk_entry_completion_set_popup_completion(completion, TRUE);
6976     gtk_entry_set_completion(entry, completion);
6978     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6980     g_object_unref(completion);
6983 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6984                                       void */*property*/,
6985                                       GObject *tbl)
6987   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6989   gboolean shown;
6990   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6991   if (shown) {
6992          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6993          //g_print("POP: notify: SHOWN\n");
6994   } else {
6995          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6997          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6998          // find out if the drop down was closed by Enter and if so, manually update (only
6999          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
7000          GdkEvent *ev = gtk_get_current_event();
7001          if (ev) {
7002              //g_print ("ev type: %d\n", ev->type);
7003              if (ev->type == GDK_KEY_PRESS) {
7004                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
7005                      case GDK_KP_Enter: // chosen
7006                      case GDK_Return:
7007                      {
7008                          // make sure the chosen one is inserted into the entry
7009                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
7010                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
7011                          GtkTreeIter iter;
7012                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
7013                          if (has_active) {
7014                              gchar *family;
7015                              gtk_tree_model_get(model, &iter, 0, &family, -1);
7016                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
7017                              gtk_entry_set_text (GTK_ENTRY (entry), family);
7018                          }
7020                          // update
7021                          sp_text_toolbox_family_changed (NULL, tbl); 
7022                          break;
7023                      }
7024                  } 
7025              }
7026          }
7028          // regardless of whether we updated, defocus the widget
7029          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
7030          if (desktop)
7031              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
7032          //g_print("POP: notify: HIDDEN\n");
7033   }
7036 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
7038     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
7039     GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
7041     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
7042     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
7044     GtkTooltips *tt = gtk_tooltips_new();
7046     ////////////Family
7047     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
7048     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
7050     gtk_rc_parse_string (
7051        "style \"dropdown-as-list-style\"\n"
7052        "{\n"
7053        "    GtkComboBox::appears-as-list = 1\n"
7054        "}\n"
7055        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
7056     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
7057     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
7059     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
7061     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
7062     
7063     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
7064     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
7066     // expand the field a bit so as to view more of the previews in the drop-down
7067     GtkRequisition req;
7068     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
7069     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
7071     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
7072     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7074     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7075     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown", 
7076              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7077     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7078     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7079     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7081     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7082     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7084     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7085     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7086     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7087     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7089     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7090     GtkWidget *box = gtk_event_box_new ();
7091     gtk_container_add (GTK_CONTAINER (box), image);
7092     gtk_toolbar_append_widget( tbl, box, "", "");
7093     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7094     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7095     gtk_widget_hide (GTK_WIDGET (box));
7096     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7098     ////////////Size
7099     gchar const *const sizes[] = {
7100         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7101         "16", "18", "20", "22", "24", "28",
7102         "32", "36", "40", "48", "56", "64", "72", "144"
7103     };
7105     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7106     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7107         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7108     }
7109     gtk_widget_set_size_request (cbox, 80, -1);
7110     gtk_toolbar_append_widget( tbl, cbox, "", "");
7111     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7112     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7113     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7114     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7116     ////////////Text anchor
7117     GtkWidget *group   = gtk_radio_button_new (NULL);
7118     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7119     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7121     // left
7122     GtkWidget *rbutton = group;
7123     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7124     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7125     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7127     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7128     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7129     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7130     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7132     // center
7133     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7134     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7135     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7136     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7138     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7139     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7140     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7141     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7143     // right
7144     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7145     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7146     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7147     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7149     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7150     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7151     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7152     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7154     // fill
7155     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7156     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7157     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7158     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7160     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7161     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7162     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7163     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7165     gtk_toolbar_append_widget( tbl, row, "", "");
7167     //spacer
7168     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7170     ////////////Text style
7171     row = gtk_hbox_new (FALSE, 4);
7173     // bold
7174     rbutton = gtk_toggle_button_new ();
7175     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7176     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7177     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7178     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7180     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7181     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7182     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7184     // italic
7185     rbutton = gtk_toggle_button_new ();
7186     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7187     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7188     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7189     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7191     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7192     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7193     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7195     gtk_toolbar_append_widget( tbl, row, "", "");
7197     //spacer
7198     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7200     // Text orientation
7201     group   = gtk_radio_button_new (NULL);
7202     row     = gtk_hbox_new (FALSE, 4);
7203     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7205     // horizontal
7206     rbutton = group;
7207     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7208     gtk_container_add           (GTK_CONTAINER (rbutton),
7209                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7210     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7211     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7213     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7214     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7215     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7217     // vertical
7218     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7219     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7220     gtk_container_add           (GTK_CONTAINER (rbutton),
7221                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7222     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7223     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7225     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7226     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7227     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7228     gtk_toolbar_append_widget( tbl, row, "", "" );
7231     //watch selection
7232     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7234     sigc::connection *c_selection_changed =
7235         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7236                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7237     pool->add_connection ("selection-changed", c_selection_changed);
7239     sigc::connection *c_selection_modified =
7240         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7241                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7242     pool->add_connection ("selection-modified", c_selection_modified);
7244     sigc::connection *c_subselection_changed =
7245         new sigc::connection (desktop->connectToolSubselectionChanged
7246                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7247     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7249     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7252     gtk_widget_show_all( GTK_WIDGET(tbl) );
7254     return GTK_WIDGET(tbl);
7255 } // end of sp_text_toolbox_new()
7257 }//<unnamed> namespace
7260 //#########################
7261 //##      Connector      ##
7262 //#########################
7264 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7266     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7267     prefs->setBool("/tools/connector/mode",
7268                 gtk_toggle_action_get_active( act ));
7271 static void sp_connector_path_set_avoid(void)
7273     cc_selection_set_avoid(true);
7277 static void sp_connector_path_set_ignore(void)
7279     cc_selection_set_avoid(false);
7282 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7284     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7285     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7286     SPDocument *doc = sp_desktop_document(desktop);
7288     if (!sp_document_get_undo_sensitive(doc))
7289     {
7290         return;
7291     }
7294     // quit if run by the _changed callbacks
7295     if (g_object_get_data( tbl, "freeze" )) {
7296         return;
7297     }
7299     // in turn, prevent callbacks from responding
7300     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7302     bool is_orthog = gtk_toggle_action_get_active( act );
7303     gchar orthog_str[] = "orthogonal";
7304     gchar polyline_str[] = "polyline";
7305     gchar *value = is_orthog ? orthog_str : polyline_str ;
7307     bool modmade = false;
7308     GSList *l = (GSList *) selection->itemList();
7309     while (l) {
7310         SPItem *item = (SPItem *) l->data;
7312         if (cc_item_is_connector(item)) {
7313             sp_object_setAttribute(item, "inkscape:connector-type",
7314                     value, false);
7315             item->avoidRef->handleSettingChange();
7316             modmade = true;
7317         }
7318         l = l->next;
7319     }
7321     if (!modmade)
7322     {
7323         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7324         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7325     }
7327     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7328             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7330     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7333 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7335     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7336     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7337     SPDocument *doc = sp_desktop_document(desktop);
7339     if (!sp_document_get_undo_sensitive(doc))
7340     {
7341         return;
7342     }
7345     // quit if run by the _changed callbacks
7346     if (g_object_get_data( tbl, "freeze" )) {
7347         return;
7348     }
7350     // in turn, prevent callbacks from responding
7351     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7353     gdouble newValue = gtk_adjustment_get_value(adj);
7354     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7355     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7357     bool modmade = false;
7358     GSList *l = (GSList *) selection->itemList();
7359     while (l) {
7360         SPItem *item = (SPItem *) l->data;
7362         if (cc_item_is_connector(item)) {
7363             sp_object_setAttribute(item, "inkscape:connector-curvature",
7364                     value, false);
7365             item->avoidRef->handleSettingChange();
7366             modmade = true;
7367         }
7368         l = l->next;
7369     }
7371     if (!modmade)
7372     {
7373         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7374         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7375     }
7377     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7378             _("Change connector curvature"));
7380     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7384 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7386     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7387     SPDocument *doc = sp_desktop_document(desktop);
7389     if (!sp_document_get_undo_sensitive(doc))
7390     {
7391         return;
7392     }
7394     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7396     if ( !repr->attribute("inkscape:connector-spacing") &&
7397             ( adj->value == defaultConnSpacing )) {
7398         // Don't need to update the repr if the attribute doesn't 
7399         // exist and it is being set to the default value -- as will
7400         // happen at startup.
7401         return;
7402     }
7404     // quit if run by the attr_changed listener
7405     if (g_object_get_data( tbl, "freeze" )) {
7406         return;
7407     }
7409     // in turn, prevent listener from responding
7410     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7412     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7413     SP_OBJECT(desktop->namedview)->updateRepr();
7415     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7416     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7417         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7418         Geom::Matrix m = Geom::identity();
7419         avoid_item_move(&m, item);
7420     }
7422     if (items) {
7423         g_slist_free(items);
7424     }
7426     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7427             _("Change connector spacing"));
7429     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7432 static void sp_connector_graph_layout(void)
7434     if (!SP_ACTIVE_DESKTOP) return;
7435     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7437     // hack for clones, see comment in align-and-distribute.cpp
7438     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7439     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7441     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7443     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7445     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7448 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7450     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7451     prefs->setBool("/tools/connector/directedlayout",
7452                 gtk_toggle_action_get_active( act ));
7455 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7457     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7458     prefs->setBool("/tools/connector/avoidoverlaplayout",
7459                 gtk_toggle_action_get_active( act ));
7463 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7465     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7466     prefs->setDouble("/tools/connector/length", adj->value);
7469 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7470                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7471                                             bool /*is_interactive*/, gpointer data)
7473     GtkWidget *tbl = GTK_WIDGET(data);
7475     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7476         return;
7477     }
7478     if (strcmp(name, "inkscape:connector-spacing") == 0)
7479     {
7480         GtkAdjustment *adj = (GtkAdjustment*)
7481                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7482         gdouble spacing = defaultConnSpacing;
7483         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7485         gtk_adjustment_set_value(adj, spacing);
7486         gtk_adjustment_value_changed(adj);
7487     }
7489     spinbutton_defocus(GTK_OBJECT(tbl));
7492 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7494     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7495     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7497     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7498         cc_create_connection_point(cc);
7501 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7503     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7504     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7506     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7507         cc_remove_connection_point(cc);
7510 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7511     NULL, /* child_added */
7512     NULL, /* child_removed */
7513     connector_tb_event_attr_changed,
7514     NULL, /* content_changed */
7515     NULL  /* order_changed */
7516 };
7518 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7520     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7521     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7522     SPItem *item = selection->singleItem();
7523     if (SP_IS_PATH(item))
7524     {
7525         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7526         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7527         gtk_toggle_action_set_active(act, is_orthog);
7528         gtk_adjustment_set_value(adj, curvature);
7529     }
7533 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7535     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7536     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7538     // Editing mode toggle button
7539     {
7540         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7541                                                       _("EditMode"),
7542                                                       _("Switch between connection point editing and connector drawing mode"),
7543                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7544                                                       Inkscape::ICON_SIZE_DECORATION );
7545         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7547         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7548         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7549         g_object_set_data( holder, "mode", act );
7550         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7551     }
7554     {
7555         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7556                                           _("Avoid"),
7557                                           _("Make connectors avoid selected objects"),
7558                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7559                                           secondarySize );
7560         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7561         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7562     }
7564     {
7565         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7566                                           _("Ignore"),
7567                                           _("Make connectors ignore selected objects"),
7568                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7569                                           secondarySize );
7570         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7571         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7572     }
7574     // Orthogonal connectors toggle button
7575     {
7576         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7577                                                       _("Orthogonal"),
7578                                                       _("Make connector orthogonal or polyline"),
7579                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7580                                                       Inkscape::ICON_SIZE_DECORATION );
7581         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7583         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7584         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7585         g_object_set_data( holder, "orthogonal", act );
7586         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7587     }
7589     EgeAdjustmentAction* eact = 0;
7590     // Curvature spinbox
7591     eact = create_adjustment_action( "ConnectorCurvatureAction",
7592                                     _("Connector Curvature"), _("Curvature:"),
7593                                     _("The amount of connectors curvature"),
7594                                     "/tools/connector/curvature", defaultConnCurvature,
7595                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7596                                     0, 100, 1.0, 10.0,
7597                                     0, 0, 0,
7598                                     connector_curvature_changed, 1, 0 );
7599     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7601     // Spacing spinbox
7602     eact = create_adjustment_action( "ConnectorSpacingAction",
7603                                     _("Connector Spacing"), _("Spacing:"),
7604                                     _("The amount of space left around objects by auto-routing connectors"),
7605                                     "/tools/connector/spacing", defaultConnSpacing,
7606                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7607                                     0, 100, 1.0, 10.0,
7608                                     0, 0, 0,
7609                                     connector_spacing_changed, 1, 0 );
7610     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7612     // Graph (connector network) layout
7613     {
7614         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7615                                           _("Graph"),
7616                                           _("Nicely arrange selected connector network"),
7617                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7618                                           secondarySize );
7619         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7620         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7621     }
7623     // Default connector length spinbox
7624     eact = create_adjustment_action( "ConnectorLengthAction",
7625                                      _("Connector Length"), _("Length:"),
7626                                      _("Ideal length for connectors when layout is applied"),
7627                                      "/tools/connector/length", 100,
7628                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7629                                      10, 1000, 10.0, 100.0,
7630                                      0, 0, 0,
7631                                      connector_length_changed, 1, 0 );
7632     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7635     // Directed edges toggle button
7636     {
7637         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7638                                                       _("Downwards"),
7639                                                       _("Make connectors with end-markers (arrows) point downwards"),
7640                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7641                                                       Inkscape::ICON_SIZE_DECORATION );
7642         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7644         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7645         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7647         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7648         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7649         );
7650     }
7652     // Avoid overlaps toggle button
7653     {
7654         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7655                                                       _("Remove overlaps"),
7656                                                       _("Do not allow overlapping shapes"),
7657                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7658                                                       Inkscape::ICON_SIZE_DECORATION );
7659         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7661         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7662         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7664         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7665     }
7668     // New connection point button
7669     {
7670         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7671                                           _("New connection point"),
7672                                           _("Add a new connection point to the currently selected item"),
7673                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7674                                           secondarySize );
7675         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7676         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7677     }
7679     // Remove selected connection point button
7681     {
7682         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7683                                           _("Remove connection point"),
7684                                           _("Remove the currently selected connection point"),
7685                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7686                                           secondarySize );
7687         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7688         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7689     }
7692     // Code to watch for changes to the connector-spacing attribute in
7693     // the XML.
7694     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7695     g_assert(repr != NULL);
7697     purge_repr_listener( holder, holder );
7699     if (repr) {
7700         g_object_set_data( holder, "repr", repr );
7701         Inkscape::GC::anchor(repr);
7702         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7703         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7704     }
7705 } // end of sp_connector_toolbox_prep()
7708 //#########################
7709 //##     Paintbucket     ##
7710 //#########################
7712 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7714     gint channels = ege_select_one_action_get_active( act );
7715     flood_channels_set_channels( channels );
7718 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7720     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7721     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7724 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7726     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7727     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7730 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7732     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7733     SPUnit const *unit = tracker->getActiveUnit();
7734     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7736     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7737     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7740 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7742     // FIXME: make defaults settable via Inkscape Options
7743     struct KeyValue {
7744         char const *key;
7745         double value;
7746     } const key_values[] = {
7747         {"threshold", 15},
7748         {"offset", 0.0}
7749     };
7751     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7752         KeyValue const &kv = key_values[i];
7753         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7754         if ( adj ) {
7755             gtk_adjustment_set_value(adj, kv.value);
7756         }
7757     }
7759     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7760     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7761     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7762     ege_select_one_action_set_active( autogap_action, 0 );
7765 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7767     EgeAdjustmentAction* eact = 0;
7768     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7770     {
7771         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7773         GList* items = 0;
7774         gint count = 0;
7775         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7776         {
7777             GtkTreeIter iter;
7778             gtk_list_store_append( model, &iter );
7779             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7780             count++;
7781         }
7782         g_list_free( items );
7783         items = 0;
7784         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7785         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7786         ege_select_one_action_set_appearance( act1, "compact" );
7787         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7788         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7789         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7790         g_object_set_data( holder, "channels_action", act1 );
7791     }
7793     // Spacing spinbox
7794     {
7795         eact = create_adjustment_action(
7796             "ThresholdAction",
7797             _("Fill Threshold"), _("Threshold:"),
7798             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7799             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7800             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7801             0, 0, 0,
7802             paintbucket_threshold_changed, 1, 0 );
7804         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7805         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7806     }
7808     // Create the units menu.
7809     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7810     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7811     if (!stored_unit.empty())
7812         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7813     g_object_set_data( holder, "tracker", tracker );
7814     {
7815         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7816         gtk_action_group_add_action( mainActions, act );
7817     }
7819     // Offset spinbox
7820     {
7821         eact = create_adjustment_action(
7822             "OffsetAction",
7823             _("Grow/shrink by"), _("Grow/shrink by:"),
7824             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7825             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7826             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7827             0, 0, 0,
7828             paintbucket_offset_changed, 1, 2);
7829         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7831         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7832     }
7834     /* Auto Gap */
7835     {
7836         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7838         GList* items = 0;
7839         gint count = 0;
7840         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7841         {
7842             GtkTreeIter iter;
7843             gtk_list_store_append( model, &iter );
7844             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7845             count++;
7846         }
7847         g_list_free( items );
7848         items = 0;
7849         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7850         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7851         ege_select_one_action_set_appearance( act2, "compact" );
7852         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7853         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7854         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7855         g_object_set_data( holder, "autogap_action", act2 );
7856     }
7858     /* Reset */
7859     {
7860         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7861                                           _("Defaults"),
7862                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7863                                           GTK_STOCK_CLEAR );
7864         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7865         gtk_action_group_add_action( mainActions, act );
7866         gtk_action_set_sensitive( act, TRUE );
7867     }
7871 /*
7872   Local Variables:
7873   mode:c++
7874   c-file-style:"stroustrup"
7875   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7876   indent-tabs-mode:nil
7877   fill-column:99
7878   End:
7879 */
7880 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :