Code

Better node transform handles behavior
[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) nt->_multipath->insertNodes();
1039 void
1040 sp_node_path_edit_delete(void)
1042     InkNodeTool *nt = get_node_tool();
1043     if (nt) nt->_multipath->deleteNodes();
1046 void
1047 sp_node_path_edit_delete_segment(void)
1049     InkNodeTool *nt = get_node_tool();
1050     if (nt) nt->_multipath->deleteSegments();
1053 void
1054 sp_node_path_edit_break(void)
1056     InkNodeTool *nt = get_node_tool();
1057     if (nt) nt->_multipath->breakNodes();
1060 void
1061 sp_node_path_edit_join(void)
1063     InkNodeTool *nt = get_node_tool();
1064     if (nt) nt->_multipath->joinNodes();
1067 void
1068 sp_node_path_edit_join_segment(void)
1070     InkNodeTool *nt = get_node_tool();
1071     if (nt) nt->_multipath->joinSegments();
1074 void
1075 sp_node_path_edit_toline(void)
1077     InkNodeTool *nt = get_node_tool();
1078     if (nt) nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_STRAIGHT);
1081 void
1082 sp_node_path_edit_tocurve(void)
1084     InkNodeTool *nt = get_node_tool();
1085     if (nt) nt->_multipath->setSegmentType(Inkscape::UI::SEGMENT_CUBIC_BEZIER);
1088 void
1089 sp_node_path_edit_cusp(void)
1091     InkNodeTool *nt = get_node_tool();
1092     if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_CUSP);
1095 void
1096 sp_node_path_edit_smooth(void)
1098     InkNodeTool *nt = get_node_tool();
1099     if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_SMOOTH);
1102 void
1103 sp_node_path_edit_symmetrical(void)
1105     InkNodeTool *nt = get_node_tool();
1106     if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_SYMMETRIC);
1109 void
1110 sp_node_path_edit_auto(void)
1112     InkNodeTool *nt = get_node_tool();
1113     if (nt) nt->_multipath->setNodeType(Inkscape::UI::NODE_AUTO);
1116 static void toggle_show_transform_handles (GtkToggleAction *act, gpointer /*data*/) {
1117     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1118     bool show = gtk_toggle_action_get_active( act );
1119     prefs->setBool("/tools/nodes/show_transform_handles", show);
1122 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1123     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1124     bool show = gtk_toggle_action_get_active( act );
1125     prefs->setBool("/tools/nodes/show_handles",  show);
1128 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1129     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1130     bool show = gtk_toggle_action_get_active( act );
1131     prefs->setBool("/tools/nodes/show_outline",  show);
1134 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1135     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1138 void toggle_edit_clip (GtkToggleAction *act, gpointer data) {
1139     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1140     bool edit = gtk_toggle_action_get_active( act );
1141     prefs->setBool("/tools/nodes/edit_clipping_paths", edit);
1144 void toggle_edit_mask (GtkToggleAction *act, gpointer data) {
1145     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1146     bool edit = gtk_toggle_action_get_active( act );
1147     prefs->setBool("/tools/nodes/edit_masks", edit);
1150 /* is called when the node selection is modified */
1151 static void
1152 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1154     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1155     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1156     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1157     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1159     // quit if run by the attr_changed listener
1160     if (g_object_get_data( tbl, "freeze" )) {
1161         return;
1162     }
1164     // in turn, prevent listener from responding
1165     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1167     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1168     SPUnit const *unit = tracker->getActiveUnit();
1170     InkNodeTool *nt = get_node_tool();
1171     if (!nt || nt->_selected_nodes->empty()) {
1172         // no path selected
1173         gtk_action_set_sensitive(xact, FALSE);
1174         gtk_action_set_sensitive(yact, FALSE);
1175     } else {
1176         gtk_action_set_sensitive(xact, TRUE);
1177         gtk_action_set_sensitive(yact, TRUE);
1178         Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1179         Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1180         Geom::Point mid = nt->_selected_nodes->pointwiseBounds()->midpoint();
1182         if (oldx != mid[Geom::X])
1183             gtk_adjustment_set_value(xadj, sp_pixels_get_units(mid[Geom::X], *unit));
1184         if (oldy != mid[Geom::Y])
1185             gtk_adjustment_set_value(yadj, sp_pixels_get_units(mid[Geom::Y], *unit));
1186     }
1188     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1191 static void
1192 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, Geom::Dim2 d)
1194     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1195     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1197     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1198     SPUnit const *unit = tracker->getActiveUnit();
1200     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1201         prefs->setDouble(Glib::ustring("/tools/nodes/") + (d == Geom::X ? "x" : "y"),
1202             sp_units_get_pixels(adj->value, *unit));
1203     }
1205     // quit if run by the attr_changed listener
1206     if (g_object_get_data( tbl, "freeze" )) {
1207         return;
1208     }
1210     // in turn, prevent listener from responding
1211     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1213     InkNodeTool *nt = get_node_tool();
1214     if (nt && !nt->_selected_nodes->empty()) {
1215         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1216         double oldval = nt->_selected_nodes->pointwiseBounds()->midpoint()[d];
1217         Geom::Point delta(0,0);
1218         delta[d] = val - oldval;
1219         nt->_multipath->move(delta);
1220     }
1222     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1225 static void
1226 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1228     sp_node_path_value_changed(adj, tbl, Geom::X);
1231 static void
1232 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1234     sp_node_path_value_changed(adj, tbl, Geom::Y);
1237 void
1238 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1240     {
1241     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1242     SPItem *item = selection->singleItem();
1243     if (item && SP_IS_LPE_ITEM(item)) {
1244        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1245            gtk_action_set_sensitive(w, TRUE);
1246        } else {
1247            gtk_action_set_sensitive(w, FALSE);
1248        }
1249     } else {
1250        gtk_action_set_sensitive(w, FALSE);
1251     }
1252     }
1255 void
1256 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1258     sp_node_toolbox_sel_changed (selection, tbl);
1263 //################################
1264 //##    Node Editing Toolbox    ##
1265 //################################
1267 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1269     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1270     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1271     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1272     g_object_set_data( holder, "tracker", tracker );
1274     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1276     {
1277         InkAction* inky = ink_action_new( "NodeInsertAction",
1278                                           _("Insert node"),
1279                                           _("Insert new nodes into selected segments"),
1280                                           INKSCAPE_ICON_NODE_ADD,
1281                                           secondarySize );
1282         g_object_set( inky, "short_label", _("Insert"), NULL );
1283         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1284         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1285     }
1287     {
1288         InkAction* inky = ink_action_new( "NodeDeleteAction",
1289                                           _("Delete node"),
1290                                           _("Delete selected nodes"),
1291                                           INKSCAPE_ICON_NODE_DELETE,
1292                                           secondarySize );
1293         g_object_set( inky, "short_label", _("Delete"), NULL );
1294         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1295         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1296     }
1298     {
1299         InkAction* inky = ink_action_new( "NodeJoinAction",
1300                                           _("Join nodes"),
1301                                           _("Join selected nodes"),
1302                                           INKSCAPE_ICON_NODE_JOIN,
1303                                           secondarySize );
1304         g_object_set( inky, "short_label", _("Join"), NULL );
1305         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1306         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1307     }
1309     {
1310         InkAction* inky = ink_action_new( "NodeBreakAction",
1311                                           _("Break nodes"),
1312                                           _("Break path at selected nodes"),
1313                                           INKSCAPE_ICON_NODE_BREAK,
1314                                           secondarySize );
1315         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1316         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1317     }
1320     {
1321         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1322                                           _("Join with segment"),
1323                                           _("Join selected endnodes with a new segment"),
1324                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1325                                           secondarySize );
1326         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1327         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328     }
1330     {
1331         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1332                                           _("Delete segment"),
1333                                           _("Delete segment between two non-endpoint nodes"),
1334                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1335                                           secondarySize );
1336         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1337         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338     }
1340     {
1341         InkAction* inky = ink_action_new( "NodeCuspAction",
1342                                           _("Node Cusp"),
1343                                           _("Make selected nodes corner"),
1344                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1345                                           secondarySize );
1346         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1347         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348     }
1350     {
1351         InkAction* inky = ink_action_new( "NodeSmoothAction",
1352                                           _("Node Smooth"),
1353                                           _("Make selected nodes smooth"),
1354                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1355                                           secondarySize );
1356         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1357         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358     }
1360     {
1361         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1362                                           _("Node Symmetric"),
1363                                           _("Make selected nodes symmetric"),
1364                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1365                                           secondarySize );
1366         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1367         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368     }
1370     {
1371         InkAction* inky = ink_action_new( "NodeAutoAction",
1372                                           _("Node Auto"),
1373                                           _("Make selected nodes auto-smooth"),
1374                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1375                                           secondarySize );
1376         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1377         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1378     }
1380     {
1381         InkAction* inky = ink_action_new( "NodeLineAction",
1382                                           _("Node Line"),
1383                                           _("Make selected segments lines"),
1384                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1385                                           secondarySize );
1386         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1387         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1388     }
1390     {
1391         InkAction* inky = ink_action_new( "NodeCurveAction",
1392                                           _("Node Curve"),
1393                                           _("Make selected segments curves"),
1394                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1395                                           secondarySize );
1396         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1397         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398     }
1400     {
1401         InkToggleAction* act = ink_toggle_action_new( "NodesShowTransformHandlesAction",
1402                                                       _("Show Transform Handles"),
1403                                                       _("Show node transformation handles"),
1404                                                       "node-transform",
1405                                                       secondarySize );
1406         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1407         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_transform_handles), desktop );
1408         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_transform_handles", false) );
1409     }
1411     {
1412         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1413                                                       _("Show Handles"),
1414                                                       _("Show the Bezier handles of selected nodes"),
1415                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1416                                                       secondarySize );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1418         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1419         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1420     }
1422     {
1423         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1424                                                       _("Show Outline"),
1425                                                       _("Show the outline of the path"),
1426                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1427                                                       secondarySize );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1429         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1430         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_outline", false) );
1431     }
1433     {
1434         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1435                                           _("Next path effect parameter"),
1436                                           _("Show next path effect parameter for editing"),
1437                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1438                                           secondarySize );
1439         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1440         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1441         g_object_set_data( holder, "nodes_lpeedit", inky);
1442     }
1444     {
1445         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditClipPathAction",
1446                                           _("Edit clipping paths"),
1447                                           _("Show editing controls for clipping paths of selected objects"),
1448                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1449                                           secondarySize );
1450         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1451         g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_clip), desktop );
1452         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_clipping_paths") );
1453     }
1455     {
1456         InkToggleAction* inky = ink_toggle_action_new( "ObjectEditMaskPathAction",
1457                                           _("Edit masks"),
1458                                           _("Show editing controls for masks of selected objects"),
1459                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1460                                           secondarySize );
1461         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1462         g_signal_connect_after( G_OBJECT(inky), "toggled", G_CALLBACK(toggle_edit_mask), desktop );
1463         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(inky), prefs->getBool("/tools/nodes/edit_masks") );
1464     }
1466     /* X coord of selected node(s) */
1467     {
1468         EgeAdjustmentAction* eact = 0;
1469         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1470         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1471         eact = create_adjustment_action( "NodeXAction",
1472                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1473                                          "/tools/nodes/Xcoord", 0,
1474                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1475                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1476                                          labels, values, G_N_ELEMENTS(labels),
1477                                          sp_node_path_x_value_changed );
1478         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1479         g_object_set_data( holder, "nodes_x_action", eact );
1480         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1481         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1482     }
1484     /* Y coord of selected node(s) */
1485     {
1486         EgeAdjustmentAction* eact = 0;
1487         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1488         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1489         eact = create_adjustment_action( "NodeYAction",
1490                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1491                                          "/tools/nodes/Ycoord", 0,
1492                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1493                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1494                                          labels, values, G_N_ELEMENTS(labels),
1495                                          sp_node_path_y_value_changed );
1496         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1497         g_object_set_data( holder, "nodes_y_action", eact );
1498         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1499         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1500     }
1502     // add the units menu
1503     {
1504         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1505         gtk_action_group_add_action( mainActions, act );
1506     }
1509     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1511     //watch selection
1512     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1514     sigc::connection *c_selection_changed =
1515         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1516                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1517     pool->add_connection ("selection-changed", c_selection_changed);
1519     sigc::connection *c_selection_modified =
1520         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1521                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1522     pool->add_connection ("selection-modified", c_selection_modified);
1524     sigc::connection *c_subselection_changed =
1525         new sigc::connection (desktop->connectToolSubselectionChanged
1526                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1527     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1529     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1531     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1532 } // end of sp_node_toolbox_prep()
1535 //########################
1536 //##    Zoom Toolbox    ##
1537 //########################
1539 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1541     // no custom GtkAction setup needed
1542 } // end of sp_zoom_toolbox_prep()
1544 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1546     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1547                                                                               "event_context_connection"));
1549     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1551     SetupFunction setup_func = 0;
1552     UpdateFunction update_func = 0;
1554     switch (id) {
1555         case BAR_TOOL:
1556             setup_func = setup_tool_toolbox;
1557             update_func = update_tool_toolbox;
1558             break;
1560         case BAR_AUX:
1561             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1562             setup_func = setup_aux_toolbox;
1563             update_func = update_aux_toolbox;
1564             break;
1566         case BAR_COMMANDS:
1567             setup_func = setup_commands_toolbox;
1568             update_func = update_commands_toolbox;
1569             break;
1571         case BAR_SNAP:
1572             setup_func = setup_snap_toolbox;
1573             update_func = updateSnapToolbox;
1574             break;
1575         default:
1576             g_warning("Unexpected toolbox id encountered.");
1577     }
1579     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1580     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1582     if (old_desktop) {
1583         GList *children, *iter;
1585         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1586         for ( iter = children ; iter ; iter = iter->next ) {
1587             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1588         }
1589         g_list_free(children);
1590     }
1592     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1594     if (desktop && setup_func && update_func) {
1595         gtk_widget_set_sensitive(toolbox, TRUE);
1596         setup_func(toolbox, desktop);
1597         update_func(desktop, desktop->event_context, toolbox);
1598         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1599     } else {
1600         gtk_widget_set_sensitive(toolbox, FALSE);
1601     }
1603 } // end of sp_toolbox_set_desktop()
1606 static void setupToolboxCommon( GtkWidget *toolbox,
1607                                 SPDesktop *desktop,
1608                                 gchar const *descr,
1609                                 gchar const* toolbarName,
1610                                 gchar const* sizePref )
1612     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1613     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1615     GtkUIManager* mgr = gtk_ui_manager_new();
1616     GError* errVal = 0;
1618     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1620     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1621     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1623     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1624     if ( prefs->getBool("/toolbox/icononly", true) ) {
1625         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1626     }
1628     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1629     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1631     if (GTK_IS_HANDLE_BOX(toolbox)) {
1632         // g_message("GRABBING ORIENTATION   [%s]", toolbarName);
1633         GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1634         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1635     } else {
1636         GtkPositionType pos = static_cast<GtkPositionType>(GPOINTER_TO_INT(g_object_get_data( G_OBJECT(toolbox), HANDLE_POS_MARK )));
1637         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1638     }
1639     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1640     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1642     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1644     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1645     if ( child ) {
1646         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1647     }
1649     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1652 #define noDUMP_DETAILS 1
1654 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1656 #if DUMP_DETAILS
1657     g_message("Set orientation for %p to be %d", toolbox, orientation);
1658     GType type = GTK_WIDGET_TYPE(toolbox);
1659     g_message("        [%s]", g_type_name(type));
1660     g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1661 #endif
1663     GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1664     GtkHandleBox* handleBox = 0;
1666     if (GTK_IS_BIN(toolbox)) {
1667 #if DUMP_DETAILS
1668         g_message("            is a BIN");
1669 #endif // DUMP_DETAILS
1670         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1671         if (child) {
1672 #if DUMP_DETAILS
1673             GType type2 = GTK_WIDGET_TYPE(child);
1674             g_message("            child    [%s]", g_type_name(type2));
1675 #endif // DUMP_DETAILS
1677             if (GTK_IS_BOX(child)) {
1678 #if DUMP_DETAILS
1679                 g_message("                is a BOX");
1680 #endif // DUMP_DETAILS
1682                 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1683                 if (children) {
1684                     for (GList* curr = children; curr; curr = g_list_next(curr)) {
1685                         GtkWidget* child2 = GTK_WIDGET(curr->data);
1686 #if DUMP_DETAILS
1687                         GType type3 = GTK_WIDGET_TYPE(child2);
1688                         g_message("                child2   [%s]", g_type_name(type3));
1689 #endif // DUMP_DETAILS
1691                         if (GTK_IS_CONTAINER(child2)) {
1692                             GList* children2 = gtk_container_get_children(GTK_CONTAINER(child2));
1693                             if (children2) {
1694                                 for (GList* curr2 = children2; curr2; curr2 = g_list_next(curr2)) {
1695                                     GtkWidget* child3 = GTK_WIDGET(curr2->data);
1696 #if DUMP_DETAILS
1697                                     GType type4 = GTK_WIDGET_TYPE(child3);
1698                                     g_message("                    child3   [%s]", g_type_name(type4));
1699 #endif // DUMP_DETAILS
1700                                     if (GTK_IS_TOOLBAR(child3)) {
1701                                         GtkToolbar* childBar = GTK_TOOLBAR(child3);
1702                                         gtk_toolbar_set_orientation(childBar, orientation);
1703                                     }
1704                                 }
1705                                 g_list_free(children2);
1706                             }
1707                         }
1710                         if (GTK_IS_TOOLBAR(child2)) {
1711                             GtkToolbar* childBar = GTK_TOOLBAR(child2);
1712                             gtk_toolbar_set_orientation(childBar, orientation);
1713                             if (GTK_IS_HANDLE_BOX(toolbox)) {
1714                                 handleBox = GTK_HANDLE_BOX(toolbox);
1715                             }
1716                         } else {
1717                             g_message("need to add dynamic switch");
1718                         }
1719                     }
1720                     g_list_free(children);
1721                 } else {
1722                     // The call is being made before the toolbox proper has been setup.
1723                     if (GTK_IS_HANDLE_BOX(toolbox)) {
1724                         handleBox = GTK_HANDLE_BOX(toolbox);
1725                     } else {
1726                         g_object_set_data(G_OBJECT(toolbox), HANDLE_POS_MARK, GINT_TO_POINTER(pos));
1727                     }
1728                 }
1729             } else if (GTK_IS_TOOLBAR(child)) {
1730                 GtkToolbar* toolbar = GTK_TOOLBAR(child);
1731                 gtk_toolbar_set_orientation( toolbar, orientation );
1732                 if (GTK_IS_HANDLE_BOX(toolbox)) {
1733                     handleBox = GTK_HANDLE_BOX(toolbox);
1734                 }
1735             }
1736         }
1737     }
1739     if (handleBox) {
1740         gtk_handle_box_set_handle_position(handleBox, pos);
1741     }
1744 static void
1745 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1747     gchar const * descr =
1748         "<ui>"
1749         "  <toolbar name='ToolToolbar'>"
1750         "    <toolitem action='ToolSelector' />"
1751         "    <toolitem action='ToolNode' />"
1752         "    <toolitem action='ToolTweak' />"
1753         "    <toolitem action='ToolSpray' />"
1754         "    <toolitem action='ToolZoom' />"
1755         "    <toolitem action='ToolRect' />"
1756         "    <toolitem action='Tool3DBox' />"
1757         "    <toolitem action='ToolArc' />"
1758         "    <toolitem action='ToolStar' />"
1759         "    <toolitem action='ToolSpiral' />"
1760         "    <toolitem action='ToolPencil' />"
1761         "    <toolitem action='ToolPen' />"
1762         "    <toolitem action='ToolCalligraphic' />"
1763         "    <toolitem action='ToolEraser' />"
1764 //        "    <toolitem action='ToolLPETool' />"
1765         "    <toolitem action='ToolPaintBucket' />"
1766         "    <toolitem action='ToolText' />"
1767         "    <toolitem action='ToolConnector' />"
1768         "    <toolitem action='ToolGradient' />"
1769         "    <toolitem action='ToolDropper' />"
1770         "  </toolbar>"
1771         "</ui>";
1773     setupToolboxCommon( toolbox, desktop, descr,
1774                         "/ui/ToolToolbar",
1775                         "/toolbox/tools/small");
1778 static void
1779 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1781     gchar const *const tname = ( eventcontext
1782                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1783                                  : NULL );
1784     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1786     for (int i = 0 ; tools[i].type_name ; i++ ) {
1787         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1788         if ( act ) {
1789             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1790             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1791             if ( verbAct ) {
1792                 verbAct->set_active(setActive);
1793             }
1794         }
1795     }
1798 static void
1799 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1801     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1802     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1803     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1804     GtkUIManager* mgr = gtk_ui_manager_new();
1805     GError* errVal = 0;
1806     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1807     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1809     std::map<std::string, GtkWidget*> dataHolders;
1811     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1812         if ( aux_toolboxes[i].prep_func ) {
1813             // converted to GtkActions and UIManager
1815             GtkWidget* kludge = gtk_toolbar_new();
1816             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1817             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1818             dataHolders[aux_toolboxes[i].type_name] = kludge;
1819             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1820         } else {
1822             GtkWidget *sub_toolbox = 0;
1823             if (aux_toolboxes[i].create_func == NULL) {
1824                 sub_toolbox = sp_empty_toolbox_new(desktop);
1825             } else {
1826                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1827             }
1829             gtk_size_group_add_widget( grouper, sub_toolbox );
1831             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1832             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1834         }
1835     }
1837     // Second pass to create toolbars *after* all GtkActions are created
1838     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1839         if ( aux_toolboxes[i].prep_func ) {
1840             // converted to GtkActions and UIManager
1842             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1844             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1845             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1847             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1848             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1849             g_free( tmp );
1850             tmp = 0;
1852             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1853                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1854             }
1856             Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1857             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1859             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1861             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1862                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1863                 swatch->setDesktop( desktop );
1864                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1865                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1866                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1867                 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 );
1868             }
1870             gtk_widget_show_all( holder );
1871             sp_set_font_size_smaller( holder );
1873             gtk_size_group_add_widget( grouper, holder );
1875             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1876             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1877         }
1878     }
1880     g_object_unref( G_OBJECT(grouper) );
1883 static void
1884 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1886     gchar const *tname = ( eventcontext
1887                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1888                            : NULL );
1889     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1890         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1891         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1892             gtk_widget_show_all(sub_toolbox);
1893             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1894         } else {
1895             gtk_widget_hide(sub_toolbox);
1896         }
1897     }
1900 static void
1901 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1903     gchar const * descr =
1904         "<ui>"
1905         "  <toolbar name='CommandsToolbar'>"
1906         "    <toolitem action='FileNew' />"
1907         "    <toolitem action='FileOpen' />"
1908         "    <toolitem action='FileSave' />"
1909         "    <toolitem action='FilePrint' />"
1910         "    <separator />"
1911         "    <toolitem action='FileImport' />"
1912         "    <toolitem action='FileExport' />"
1913         "    <separator />"
1914         "    <toolitem action='EditUndo' />"
1915         "    <toolitem action='EditRedo' />"
1916         "    <separator />"
1917         "    <toolitem action='EditCopy' />"
1918         "    <toolitem action='EditCut' />"
1919         "    <toolitem action='EditPaste' />"
1920         "    <separator />"
1921         "    <toolitem action='ZoomSelection' />"
1922         "    <toolitem action='ZoomDrawing' />"
1923         "    <toolitem action='ZoomPage' />"
1924         "    <separator />"
1925         "    <toolitem action='EditDuplicate' />"
1926         "    <toolitem action='EditClone' />"
1927         "    <toolitem action='EditUnlinkClone' />"
1928         "    <separator />"
1929         "    <toolitem action='SelectionGroup' />"
1930         "    <toolitem action='SelectionUnGroup' />"
1931         "    <separator />"
1932         "    <toolitem action='DialogFillStroke' />"
1933         "    <toolitem action='DialogText' />"
1934         "    <toolitem action='DialogLayers' />"
1935         "    <toolitem action='DialogXMLEditor' />"
1936         "    <toolitem action='DialogAlignDistribute' />"
1937         "    <separator />"
1938         "    <toolitem action='DialogPreferences' />"
1939         "    <toolitem action='DialogDocumentProperties' />"
1940 #if ENABLE_TASK_SUPPORT
1941         "    <separator />"
1942         "    <toolitem action='TaskSetAction' />"
1943 #endif // ENABLE_TASK_SUPPORT
1944         "  </toolbar>"
1945         "</ui>";
1947     setupToolboxCommon( toolbox, desktop, descr,
1948                         "/ui/CommandsToolbar",
1949                         "/toolbox/small" );
1952 static void
1953 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1957 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1959     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1960         return;
1961     }
1963     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1964     g_assert(ptr != NULL);
1966     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1967     SPNamedView *nv = sp_desktop_namedview(dt);
1968     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1970     if (dt == NULL || nv == NULL) {
1971         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1972         return;
1973     }
1975     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1977     if (repr == NULL) {
1978         g_warning("This namedview doesn't have a xml representation attached!");
1979         return;
1980     }
1982     bool saved = sp_document_get_undo_sensitive(doc);
1983     sp_document_set_undo_sensitive(doc, false);
1985     bool v = false;
1986     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1988     switch (attr) {
1989         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1990             dt->toggleSnapGlobal();
1991             break;
1992         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1993             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1994             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1995             break;
1996         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1997             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1998             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1999             break;
2000         case SP_ATTR_INKSCAPE_BBOX_NODES:
2001             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
2002             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
2003             break;
2004         case SP_ATTR_INKSCAPE_SNAP_NODES:
2005             v = nv->snap_manager.snapprefs.getSnapModeNode();
2006             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
2007             break;
2008         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
2009             v = nv->snap_manager.snapprefs.getSnapToItemPath();
2010             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
2011             break;
2012         case SP_ATTR_INKSCAPE_OBJECT_NODES:
2013             v = nv->snap_manager.snapprefs.getSnapToItemNode();
2014             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
2015             break;
2016         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
2017             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
2018             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
2019             break;
2020         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
2021             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
2022             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
2023             break;
2024         case SP_ATTR_INKSCAPE_SNAP_CENTER:
2025             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
2026             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
2027             break;
2028         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
2029             v = nv->snap_manager.snapprefs.getSnapToGrids();
2030             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
2031             break;
2032         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
2033             v = nv->snap_manager.snapprefs.getSnapToGuides();
2034             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
2035             break;
2036         case SP_ATTR_INKSCAPE_SNAP_PAGE:
2037             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
2038             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
2039             break;
2040             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
2041               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
2042               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
2043               break;*/
2044         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
2045             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
2046             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
2047             break;
2048         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
2049             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
2050             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
2051             break;
2052         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
2053             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
2054             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
2055             break;
2056         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
2057             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
2058             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
2059             break;
2060         default:
2061             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
2062             break;
2063     }
2065     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
2066     doc->setModifiedSinceSave();
2068     sp_document_set_undo_sensitive(doc, saved);
2071 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
2073     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2075     gchar const * descr =
2076         "<ui>"
2077         "  <toolbar name='SnapToolbar'>"
2078         "    <toolitem action='ToggleSnapGlobal' />"
2079         "    <separator />"
2080         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2081         "    <toolitem action='ToggleSnapToBBoxPath' />"
2082         "    <toolitem action='ToggleSnapToBBoxNode' />"
2083         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2084         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2085         "    <separator />"
2086         "    <toolitem action='ToggleSnapFromNode' />"
2087         "    <toolitem action='ToggleSnapToItemPath' />"
2088         "    <toolitem action='ToggleSnapToPathIntersections' />"
2089         "    <toolitem action='ToggleSnapToItemNode' />"
2090         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2091         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2092         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2093         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2094         "    <separator />"
2095         "    <toolitem action='ToggleSnapToPageBorder' />"
2096         "    <toolitem action='ToggleSnapToGrids' />"
2097         "    <toolitem action='ToggleSnapToGuides' />"
2098         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2099         "  </toolbar>"
2100         "</ui>";
2102     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2104     {
2105         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2106                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2107                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2109         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2110         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2111     }
2113     {
2114         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2115                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2116                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2118         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2119         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2120     }
2122     {
2123         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2124                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2125                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2127         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2128         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2129     }
2131     {
2132         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2133                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2134                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2136         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2137         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2138     }
2140     {
2141         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2142                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2143                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2144                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2146         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2147         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2148     }
2150     {
2151         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2152                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2153                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2155         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2156         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2157     }
2159     {
2160         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2161                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2163         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2164         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2165     }
2167     {
2168         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2169                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2170                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2172         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2173         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2174     }
2176     {
2177         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2178                                                      _("Path intersections"), _("Snap to path intersections"),
2179                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2181         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2182         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2183     }
2185     {
2186         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2187                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2188                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2190         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2191         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2192     }
2194     {
2195         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2196                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2197                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2199         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2200         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2201     }
2203     {
2204         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2205                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2206                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2208         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2209         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2210     }
2212     {
2213         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2214                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2215                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2217         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2218         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2219     }
2221     {
2222         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2223                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2224                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2226         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2227         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2228     }
2230     {
2231         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2232                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2233                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2235         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2236         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2237     }
2239     {
2240         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2241                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2242                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2244         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2245         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2246     }
2248     {
2249         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2250                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2251                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2253         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2254         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2255     }
2257     /*{
2258       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2259       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2260       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2261       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2263       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2264       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2265       }*/
2267     setupToolboxCommon( toolbox, desktop, descr,
2268                         "/ui/SnapToolbar",
2269                         "/toolbox/secondary" );
2272 Glib::ustring ToolboxFactory::getToolboxName(GtkWidget* toolbox)
2274     Glib::ustring name;
2275     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
2276     switch(id) {
2277         case BAR_TOOL:
2278             name = "ToolToolbar";
2279             break;
2280         case BAR_AUX:
2281             name = "AuxToolbar";
2282             break;
2283         case BAR_COMMANDS:
2284             name = "CommandsToolbar";
2285             break;
2286         case BAR_SNAP:
2287             name = "SnapToolbar";
2288             break;
2289     }
2291     return name;
2294 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2296     g_assert(desktop != NULL);
2297     g_assert(toolbox != NULL);
2299     SPNamedView *nv = sp_desktop_namedview(desktop);
2300     if (nv == NULL) {
2301         g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2302         return;
2303     }
2305     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2307     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2308     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2309     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2310     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2311     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2312     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2313     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2314     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2315     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2316     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2317     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2318     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2319     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2320     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2321     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2322     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2323     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2324     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2327     if (!act1) {
2328         return; // The snap actions haven't been defined yet (might be the case during startup)
2329     }
2331     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2332     // changes in our document because we're only updating the UI;
2333     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2334     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2336     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2337     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2339     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2340     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2341     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2343     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2344     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2345     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2346     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2347     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2348     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2349     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2350     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2352     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2353     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2354     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2356     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2357     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2358     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2359     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2360     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2361     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2362     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2363     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2364     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2365     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2366     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2367     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2368     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2369     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2370     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2372     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2373     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2374     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2375     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2377     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2378     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2379     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2380     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2383     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2386 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2388     gtk_widget_show(toolbox_toplevel);
2389     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2391     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2392     if (!shown_toolbox) {
2393         return;
2394     }
2395     gtk_widget_show(toolbox);
2397     gtk_widget_show_all(shown_toolbox);
2400 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2402     GtkWidget *tbl = gtk_toolbar_new();
2403     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2404     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2406     gtk_widget_show_all(tbl);
2407     sp_set_font_size_smaller (tbl);
2409     return tbl;
2412 #define MODE_LABEL_WIDTH 70
2414 //########################
2415 //##       Star         ##
2416 //########################
2418 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2420     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2422     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2423         // do not remember prefs if this call is initiated by an undo change, because undoing object
2424         // creation sets bogus values to its attributes before it is deleted
2425         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2426         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2427     }
2429     // quit if run by the attr_changed listener
2430     if (g_object_get_data( dataKludge, "freeze" )) {
2431         return;
2432     }
2434     // in turn, prevent listener from responding
2435     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2437     bool modmade = false;
2439     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2440     GSList const *items = selection->itemList();
2441     for (; items != NULL; items = items->next) {
2442         if (SP_IS_STAR((SPItem *) items->data)) {
2443             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2444             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2445             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2446                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2447                                     + M_PI / (gint)adj->value));
2448             SP_OBJECT((SPItem *) items->data)->updateRepr();
2449             modmade = true;
2450         }
2451     }
2452     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2453                                    _("Star: Change number of corners"));
2455     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2458 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2460     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2462     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2463         if (!IS_NAN(adj->value)) {
2464             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2465             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2466         }
2467     }
2469     // quit if run by the attr_changed listener
2470     if (g_object_get_data( dataKludge, "freeze" )) {
2471         return;
2472     }
2474     // in turn, prevent listener from responding
2475     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2477     bool modmade = false;
2478     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2479     GSList const *items = selection->itemList();
2480     for (; items != NULL; items = items->next) {
2481         if (SP_IS_STAR((SPItem *) items->data)) {
2482             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2484             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2485             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2486             if (r2 < r1) {
2487                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2488             } else {
2489                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2490             }
2492             SP_OBJECT((SPItem *) items->data)->updateRepr();
2493             modmade = true;
2494         }
2495     }
2497     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2498                                    _("Star: Change spoke ratio"));
2500     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2503 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2505     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2506     bool flat = ege_select_one_action_get_active( act ) == 0;
2508     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2509         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2510         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2511     }
2513     // quit if run by the attr_changed listener
2514     if (g_object_get_data( dataKludge, "freeze" )) {
2515         return;
2516     }
2518     // in turn, prevent listener from responding
2519     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2521     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2522     GSList const *items = selection->itemList();
2523     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2524     bool modmade = false;
2526     if ( prop_action ) {
2527         gtk_action_set_sensitive( prop_action, !flat );
2528     }
2530     for (; items != NULL; items = items->next) {
2531         if (SP_IS_STAR((SPItem *) items->data)) {
2532             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2533             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2534             SP_OBJECT((SPItem *) items->data)->updateRepr();
2535             modmade = true;
2536         }
2537     }
2539     if (modmade) {
2540         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2541                          flat ? _("Make polygon") : _("Make star"));
2542     }
2544     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2547 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2549     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2551     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2552         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2553         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2554     }
2556     // quit if run by the attr_changed listener
2557     if (g_object_get_data( dataKludge, "freeze" )) {
2558         return;
2559     }
2561     // in turn, prevent listener from responding
2562     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2564     bool modmade = false;
2566     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2567     GSList const *items = selection->itemList();
2568     for (; items != NULL; items = items->next) {
2569         if (SP_IS_STAR((SPItem *) items->data)) {
2570             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2571             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2572             SP_OBJECT(items->data)->updateRepr();
2573             modmade = true;
2574         }
2575     }
2576     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2577                                    _("Star: Change rounding"));
2579     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2582 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2584     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2586     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2587         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2588         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2589     }
2591     // quit if run by the attr_changed listener
2592     if (g_object_get_data( dataKludge, "freeze" )) {
2593         return;
2594     }
2596     // in turn, prevent listener from responding
2597     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2599     bool modmade = false;
2601     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2602     GSList const *items = selection->itemList();
2603     for (; items != NULL; items = items->next) {
2604         if (SP_IS_STAR((SPItem *) items->data)) {
2605             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2606             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2607             SP_OBJECT(items->data)->updateRepr();
2608             modmade = true;
2609         }
2610     }
2611     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2612                                    _("Star: Change randomization"));
2614     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2618 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2619                                        gchar const */*old_value*/, gchar const */*new_value*/,
2620                                        bool /*is_interactive*/, gpointer data)
2622     GtkWidget *tbl = GTK_WIDGET(data);
2624     // quit if run by the _changed callbacks
2625     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2626         return;
2627     }
2629     // in turn, prevent callbacks from responding
2630     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2632     GtkAdjustment *adj = 0;
2634     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2635     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2637     if (!strcmp(name, "inkscape:randomized")) {
2638         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2639         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2640     } else if (!strcmp(name, "inkscape:rounded")) {
2641         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2642         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2643     } else if (!strcmp(name, "inkscape:flatsided")) {
2644         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2645         char const *flatsides = repr->attribute("inkscape:flatsided");
2646         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2647         if ( flatsides && !strcmp(flatsides,"false") ) {
2648             ege_select_one_action_set_active( flat_action, 1 );
2649             gtk_action_set_sensitive( prop_action, TRUE );
2650         } else {
2651             ege_select_one_action_set_active( flat_action, 0 );
2652             gtk_action_set_sensitive( prop_action, FALSE );
2653         }
2654     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2655         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2656         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2657         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2658         if (r2 < r1) {
2659             gtk_adjustment_set_value(adj, r2/r1);
2660         } else {
2661             gtk_adjustment_set_value(adj, r1/r2);
2662         }
2663     } else if (!strcmp(name, "sodipodi:sides")) {
2664         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2665         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2666     }
2668     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2672 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2674     NULL, /* child_added */
2675     NULL, /* child_removed */
2676     star_tb_event_attr_changed,
2677     NULL, /* content_changed */
2678     NULL  /* order_changed */
2679 };
2682 /**
2683  *  \param selection Should not be NULL.
2684  */
2685 static void
2686 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2688     int n_selected = 0;
2689     Inkscape::XML::Node *repr = NULL;
2691     purge_repr_listener( tbl, tbl );
2693     for (GSList const *items = selection->itemList();
2694          items != NULL;
2695          items = items->next)
2696     {
2697         if (SP_IS_STAR((SPItem *) items->data)) {
2698             n_selected++;
2699             repr = SP_OBJECT_REPR((SPItem *) items->data);
2700         }
2701     }
2703     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2705     if (n_selected == 0) {
2706         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2707     } else if (n_selected == 1) {
2708         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2710         if (repr) {
2711             g_object_set_data( tbl, "repr", repr );
2712             Inkscape::GC::anchor(repr);
2713             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2714             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2715         }
2716     } else {
2717         // FIXME: implement averaging of all parameters for multiple selected stars
2718         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2719         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2720     }
2724 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2726     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2727     // callbacks to lump all the changes for all selected objects in one undo step
2729     GtkAdjustment *adj = 0;
2731     // fixme: make settable in prefs!
2732     gint mag = 5;
2733     gdouble prop = 0.5;
2734     gboolean flat = FALSE;
2735     gdouble randomized = 0;
2736     gdouble rounded = 0;
2738     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2739     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2741     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2742     gtk_action_set_sensitive( sb2, !flat );
2744     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2745     gtk_adjustment_set_value(adj, mag);
2746     gtk_adjustment_value_changed(adj);
2748     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2749     gtk_adjustment_set_value(adj, prop);
2750     gtk_adjustment_value_changed(adj);
2752     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2753     gtk_adjustment_set_value(adj, rounded);
2754     gtk_adjustment_value_changed(adj);
2756     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2757     gtk_adjustment_set_value(adj, randomized);
2758     gtk_adjustment_value_changed(adj);
2762 void
2763 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2765     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2766     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2767     GtkWidget *l = gtk_label_new(NULL);
2768     gtk_label_set_markup(GTK_LABEL(l), title);
2769     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2770     if ( GTK_IS_TOOLBAR(tbl) ) {
2771         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2772     } else {
2773         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2774     }
2775     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2779 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2781     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2783     {
2784         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2785         ege_output_action_set_use_markup( act, TRUE );
2786         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2787         g_object_set_data( holder, "mode_action", act );
2788     }
2790     {
2791         EgeAdjustmentAction* eact = 0;
2792         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2793         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2795         /* Flatsided checkbox */
2796         {
2797             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2799             GtkTreeIter iter;
2800             gtk_list_store_append( model, &iter );
2801             gtk_list_store_set( model, &iter,
2802                                 0, _("Polygon"),
2803                                 1, _("Regular polygon (with one handle) instead of a star"),
2804                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2805                                 -1 );
2807             gtk_list_store_append( model, &iter );
2808             gtk_list_store_set( model, &iter,
2809                                 0, _("Star"),
2810                                 1, _("Star instead of a regular polygon (with one handle)"),
2811                                 2, INKSCAPE_ICON_DRAW_STAR,
2812                                 -1 );
2814             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2815             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2816             g_object_set_data( holder, "flat_action", act );
2818             ege_select_one_action_set_appearance( act, "full" );
2819             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2820             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2821             ege_select_one_action_set_icon_column( act, 2 );
2822             ege_select_one_action_set_icon_size( act, secondarySize );
2823             ege_select_one_action_set_tooltip_column( act, 1  );
2825             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2826             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2827         }
2829         /* Magnitude */
2830         {
2831         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2832         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2833         eact = create_adjustment_action( "MagnitudeAction",
2834                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2835                                          "/tools/shapes/star/magnitude", 3,
2836                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2837                                          3, 1024, 1, 5,
2838                                          labels, values, G_N_ELEMENTS(labels),
2839                                          sp_stb_magnitude_value_changed,
2840                                          1.0, 0 );
2841         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2842         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2843         }
2845         /* Spoke ratio */
2846         {
2847         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2848         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2849         eact = create_adjustment_action( "SpokeAction",
2850                                          _("Spoke ratio"), _("Spoke ratio:"),
2851                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2852                                          // Base radius is the same for the closest handle.
2853                                          _("Base radius to tip radius ratio"),
2854                                          "/tools/shapes/star/proportion", 0.5,
2855                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2856                                          0.01, 1.0, 0.01, 0.1,
2857                                          labels, values, G_N_ELEMENTS(labels),
2858                                          sp_stb_proportion_value_changed );
2859         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2860         g_object_set_data( holder, "prop_action", eact );
2861         }
2863         if ( !isFlatSided ) {
2864             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2865         } else {
2866             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2867         }
2869         /* Roundedness */
2870         {
2871         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2872         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2873         eact = create_adjustment_action( "RoundednessAction",
2874                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2875                                          "/tools/shapes/star/rounded", 0.0,
2876                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2877                                          -10.0, 10.0, 0.01, 0.1,
2878                                          labels, values, G_N_ELEMENTS(labels),
2879                                          sp_stb_rounded_value_changed );
2880         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2881         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2882         }
2884         /* Randomization */
2885         {
2886         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2887         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2888         eact = create_adjustment_action( "RandomizationAction",
2889                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2890                                          "/tools/shapes/star/randomized", 0.0,
2891                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2892                                          -10.0, 10.0, 0.001, 0.01,
2893                                          labels, values, G_N_ELEMENTS(labels),
2894                                          sp_stb_randomized_value_changed, 0.1, 3 );
2895         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2896         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2897         }
2898     }
2900     {
2901         /* Reset */
2902         {
2903             GtkAction* act = gtk_action_new( "StarResetAction",
2904                                              _("Defaults"),
2905                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2906                                              GTK_STOCK_CLEAR );
2907             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2908             gtk_action_group_add_action( mainActions, act );
2909             gtk_action_set_sensitive( act, TRUE );
2910         }
2911     }
2913     sigc::connection *connection = new sigc::connection(
2914         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2915         );
2916     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2917     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2921 //########################
2922 //##       Rect         ##
2923 //########################
2925 static void sp_rtb_sensitivize( GObject *tbl )
2927     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2928     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2929     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2931     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2932         gtk_action_set_sensitive( not_rounded, FALSE );
2933     } else {
2934         gtk_action_set_sensitive( not_rounded, TRUE );
2935     }
2939 static void
2940 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2941                           void (*setter)(SPRect *, gdouble))
2943     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2945     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2946     SPUnit const *unit = tracker->getActiveUnit();
2948     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2949         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2950         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2951     }
2953     // quit if run by the attr_changed listener
2954     if (g_object_get_data( tbl, "freeze" )) {
2955         return;
2956     }
2958     // in turn, prevent listener from responding
2959     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2961     bool modmade = false;
2962     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2963     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2964         if (SP_IS_RECT(items->data)) {
2965             if (adj->value != 0) {
2966                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2967             } else {
2968                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2969             }
2970             modmade = true;
2971         }
2972     }
2974     sp_rtb_sensitivize( tbl );
2976     if (modmade) {
2977         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2978                                    _("Change rectangle"));
2979     }
2981     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2984 static void
2985 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2987     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2990 static void
2991 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2993     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2996 static void
2997 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2999     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
3002 static void
3003 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
3005     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
3010 static void
3011 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
3013     GtkAdjustment *adj = 0;
3015     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
3016     gtk_adjustment_set_value(adj, 0.0);
3017     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
3018     gtk_adjustment_value_changed(adj);
3020     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
3021     gtk_adjustment_set_value(adj, 0.0);
3022     gtk_adjustment_value_changed(adj);
3024     sp_rtb_sensitivize( obj );
3027 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
3028                                        gchar const */*old_value*/, gchar const */*new_value*/,
3029                                        bool /*is_interactive*/, gpointer data)
3031     GObject *tbl = G_OBJECT(data);
3033     // quit if run by the _changed callbacks
3034     if (g_object_get_data( tbl, "freeze" )) {
3035         return;
3036     }
3038     // in turn, prevent callbacks from responding
3039     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3041     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
3042     SPUnit const *unit = tracker->getActiveUnit();
3044     gpointer item = g_object_get_data( tbl, "item" );
3045     if (item && SP_IS_RECT(item)) {
3046         {
3047             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
3048             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
3049             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
3050         }
3052         {
3053             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
3054             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
3055             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
3056         }
3058         {
3059             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
3060             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
3061             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
3062         }
3064         {
3065             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
3066             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
3067             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
3068         }
3069     }
3071     sp_rtb_sensitivize( tbl );
3073     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3077 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3078     NULL, /* child_added */
3079     NULL, /* child_removed */
3080     rect_tb_event_attr_changed,
3081     NULL, /* content_changed */
3082     NULL  /* order_changed */
3083 };
3085 /**
3086  *  \param selection should not be NULL.
3087  */
3088 static void
3089 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3091     int n_selected = 0;
3092     Inkscape::XML::Node *repr = NULL;
3093     SPItem *item = NULL;
3095     if ( g_object_get_data( tbl, "repr" ) ) {
3096         g_object_set_data( tbl, "item", NULL );
3097     }
3098     purge_repr_listener( tbl, tbl );
3100     for (GSList const *items = selection->itemList();
3101          items != NULL;
3102          items = items->next) {
3103         if (SP_IS_RECT((SPItem *) items->data)) {
3104             n_selected++;
3105             item = (SPItem *) items->data;
3106             repr = SP_OBJECT_REPR(item);
3107         }
3108     }
3110     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3112     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3114     if (n_selected == 0) {
3115         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3117         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3118         gtk_action_set_sensitive(w, FALSE);
3119         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3120         gtk_action_set_sensitive(h, FALSE);
3122     } else if (n_selected == 1) {
3123         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3124         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3126         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3127         gtk_action_set_sensitive(w, TRUE);
3128         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3129         gtk_action_set_sensitive(h, TRUE);
3131         if (repr) {
3132             g_object_set_data( tbl, "repr", repr );
3133             g_object_set_data( tbl, "item", item );
3134             Inkscape::GC::anchor(repr);
3135             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3136             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3137         }
3138     } else {
3139         // FIXME: implement averaging of all parameters for multiple selected
3140         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3141         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3142         sp_rtb_sensitivize( tbl );
3143     }
3147 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3149     EgeAdjustmentAction* eact = 0;
3150     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3152     {
3153         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3154         ege_output_action_set_use_markup( act, TRUE );
3155         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3156         g_object_set_data( holder, "mode_action", act );
3157     }
3159     // rx/ry units menu: create
3160     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3161     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3162     // fixme: add % meaning per cent of the width/height
3163     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3164     g_object_set_data( holder, "tracker", tracker );
3166     /* W */
3167     {
3168         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3169         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3170         eact = create_adjustment_action( "RectWidthAction",
3171                                          _("Width"), _("W:"), _("Width of rectangle"),
3172                                          "/tools/shapes/rect/width", 0,
3173                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3174                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3175                                          labels, values, G_N_ELEMENTS(labels),
3176                                          sp_rtb_width_value_changed );
3177         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3178         g_object_set_data( holder, "width_action", eact );
3179         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3180         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3181     }
3183     /* H */
3184     {
3185         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3186         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3187         eact = create_adjustment_action( "RectHeightAction",
3188                                          _("Height"), _("H:"), _("Height of rectangle"),
3189                                          "/tools/shapes/rect/height", 0,
3190                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3191                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3192                                          labels, values, G_N_ELEMENTS(labels),
3193                                          sp_rtb_height_value_changed );
3194         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3195         g_object_set_data( holder, "height_action", eact );
3196         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3197         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3198     }
3200     /* rx */
3201     {
3202         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3203         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3204         eact = create_adjustment_action( "RadiusXAction",
3205                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3206                                          "/tools/shapes/rect/rx", 0,
3207                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3208                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3209                                          labels, values, G_N_ELEMENTS(labels),
3210                                          sp_rtb_rx_value_changed);
3211         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3212         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3213     }
3215     /* ry */
3216     {
3217         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3218         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3219         eact = create_adjustment_action( "RadiusYAction",
3220                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3221                                          "/tools/shapes/rect/ry", 0,
3222                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3223                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3224                                          labels, values, G_N_ELEMENTS(labels),
3225                                          sp_rtb_ry_value_changed);
3226         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3227         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3228     }
3230     // add the units menu
3231     {
3232         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3233         gtk_action_group_add_action( mainActions, act );
3234     }
3236     /* Reset */
3237     {
3238         InkAction* inky = ink_action_new( "RectResetAction",
3239                                           _("Not rounded"),
3240                                           _("Make corners sharp"),
3241                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3242                                           secondarySize );
3243         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3244         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3245         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3246         g_object_set_data( holder, "not_rounded", inky );
3247     }
3249     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3250     sp_rtb_sensitivize( holder );
3252     sigc::connection *connection = new sigc::connection(
3253         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3254         );
3255     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3256     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3259 //########################
3260 //##       3D Box       ##
3261 //########################
3263 // normalize angle so that it lies in the interval [0,360]
3264 static double box3d_normalize_angle (double a) {
3265     double angle = a + ((int) (a/360.0))*360;
3266     if (angle < 0) {
3267         angle += 360.0;
3268     }
3269     return angle;
3272 static void
3273 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3274                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3275     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3276     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3277     //       are reset).
3278     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3280     if (is_infinite) {
3281         gtk_toggle_action_set_active(tact, TRUE);
3282         gtk_action_set_sensitive(act, TRUE);
3284         double angle = persp3d_get_infinite_angle(persp, axis);
3285         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3286             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3287         }
3288     } else {
3289         gtk_toggle_action_set_active(tact, FALSE);
3290         gtk_action_set_sensitive(act, FALSE);
3291     }
3294 static void
3295 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3296     if (!persp_repr) {
3297         g_print ("No perspective given to box3d_resync_toolbar().\n");
3298         return;
3299     }
3301     GtkWidget *tbl = GTK_WIDGET(data);
3302     GtkAdjustment *adj = 0;
3303     GtkAction *act = 0;
3304     GtkToggleAction *tact = 0;
3305     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3306     if (!persp) {
3307         // Hmm, is it an error if this happens?
3308         return;
3309     }
3310     {
3311         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3312         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3313         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3315         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3316     }
3317     {
3318         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3319         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3320         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3322         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3323     }
3324     {
3325         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3326         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3327         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3329         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3330     }
3333 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3334                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3335                                                   bool /*is_interactive*/, gpointer data)
3337     GtkWidget *tbl = GTK_WIDGET(data);
3339     // quit if run by the attr_changed or selection changed listener
3340     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3341         return;
3342     }
3344     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3345     // sp_document_maybe_done() when the document is undo insensitive)
3346     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3348     // TODO: Only update the appropriate part of the toolbar
3349 //    if (!strcmp(name, "inkscape:vp_z")) {
3350         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3351 //    }
3353     Persp3D *persp = persp3d_get_from_repr(repr);
3354     persp3d_update_box_reprs(persp);
3356     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3359 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3361     NULL, /* child_added */
3362     NULL, /* child_removed */
3363     box3d_persp_tb_event_attr_changed,
3364     NULL, /* content_changed */
3365     NULL  /* order_changed */
3366 };
3368 /**
3369  *  \param selection Should not be NULL.
3370  */
3371 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3372 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3373 static void
3374 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3376     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3377     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3378     // update the perspectives with infinite VPs and leave the other ones untouched).
3380     Inkscape::XML::Node *persp_repr = NULL;
3381     purge_repr_listener(tbl, tbl);
3383     SPItem *item = selection->singleItem();
3384     if (item && SP_IS_BOX3D(item)) {
3385         // FIXME: Also deal with multiple selected boxes
3386         SPBox3D *box = SP_BOX3D(item);
3387         Persp3D *persp = box3d_get_perspective(box);
3388         persp_repr = SP_OBJECT_REPR(persp);
3389         if (persp_repr) {
3390             g_object_set_data(tbl, "repr", persp_repr);
3391             Inkscape::GC::anchor(persp_repr);
3392             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3393             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3394         }
3396         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3397         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3398         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3400         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3401         box3d_resync_toolbar(persp_repr, tbl);
3402         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3403     }
3406 static void
3407 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3409     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3410     SPDocument *document = sp_desktop_document(desktop);
3412     // quit if run by the attr_changed or selection changed listener
3413     if (g_object_get_data( dataKludge, "freeze" )) {
3414         return;
3415     }
3417     // in turn, prevent listener from responding
3418     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3420     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3421     if (sel_persps.empty()) {
3422         // this can happen when the document is created; we silently ignore it
3423         return;
3424     }
3425     Persp3D *persp = sel_persps.front();
3427     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3428     SP_OBJECT(persp)->updateRepr();
3430     // TODO: use the correct axis here, too
3431     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3433     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3437 static void
3438 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3440     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3443 static void
3444 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3446     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3449 static void
3450 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3452     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3456 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3458     // TODO: Take all selected perspectives into account
3459     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3460     if (sel_persps.empty()) {
3461         // this can happen when the document is created; we silently ignore it
3462         return;
3463     }
3464     Persp3D *persp = sel_persps.front();
3466     bool set_infinite = gtk_toggle_action_get_active(act);
3467     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3470 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3472     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3475 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3477     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3480 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3482     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3485 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3487     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3488     EgeAdjustmentAction* eact = 0;
3489     SPDocument *document = sp_desktop_document (desktop);
3490     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3492     EgeAdjustmentAction* box3d_angle_x = 0;
3493     EgeAdjustmentAction* box3d_angle_y = 0;
3494     EgeAdjustmentAction* box3d_angle_z = 0;
3496     /* Angle X */
3497     {
3498         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3499         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3500         eact = create_adjustment_action( "3DBoxAngleXAction",
3501                                          _("Angle in X direction"), _("Angle X:"),
3502                                          // Translators: PL is short for 'perspective line'
3503                                          _("Angle of PLs in X direction"),
3504                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3505                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3506                                          -360.0, 360.0, 1.0, 10.0,
3507                                          labels, values, G_N_ELEMENTS(labels),
3508                                          box3d_angle_x_value_changed );
3509         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3510         g_object_set_data( holder, "box3d_angle_x_action", eact );
3511         box3d_angle_x = eact;
3512     }
3514     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3515         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3516     } else {
3517         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3518     }
3521     /* VP X state */
3522     {
3523         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3524                                                       // Translators: VP is short for 'vanishing point'
3525                                                       _("State of VP in X direction"),
3526                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3527                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3528                                                       Inkscape::ICON_SIZE_DECORATION );
3529         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3530         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3531         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3532         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3533         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3534     }
3536     /* Angle Y */
3537     {
3538         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3539         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3540         eact = create_adjustment_action( "3DBoxAngleYAction",
3541                                          _("Angle in Y direction"), _("Angle Y:"),
3542                                          // Translators: PL is short for 'perspective line'
3543                                          _("Angle of PLs in Y direction"),
3544                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3545                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3546                                          -360.0, 360.0, 1.0, 10.0,
3547                                          labels, values, G_N_ELEMENTS(labels),
3548                                          box3d_angle_y_value_changed );
3549         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3550         g_object_set_data( holder, "box3d_angle_y_action", eact );
3551         box3d_angle_y = eact;
3552     }
3554     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3555         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3556     } else {
3557         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3558     }
3560     /* VP Y state */
3561     {
3562         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3563                                                       // Translators: VP is short for 'vanishing point'
3564                                                       _("State of VP in Y direction"),
3565                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3566                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3567                                                       Inkscape::ICON_SIZE_DECORATION );
3568         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3569         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3570         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3571         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3572         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3573     }
3575     /* Angle Z */
3576     {
3577         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3578         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3579         eact = create_adjustment_action( "3DBoxAngleZAction",
3580                                          _("Angle in Z direction"), _("Angle Z:"),
3581                                          // Translators: PL is short for 'perspective line'
3582                                          _("Angle of PLs in Z direction"),
3583                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3584                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3585                                          -360.0, 360.0, 1.0, 10.0,
3586                                          labels, values, G_N_ELEMENTS(labels),
3587                                          box3d_angle_z_value_changed );
3588         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3589         g_object_set_data( holder, "box3d_angle_z_action", eact );
3590         box3d_angle_z = eact;
3591     }
3593     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3594         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3595     } else {
3596         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3597     }
3599     /* VP Z state */
3600     {
3601         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3602                                                       // Translators: VP is short for 'vanishing point'
3603                                                       _("State of VP in Z direction"),
3604                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3605                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3606                                                       Inkscape::ICON_SIZE_DECORATION );
3607         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3608         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3609         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3610         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3611         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3612     }
3614     sigc::connection *connection = new sigc::connection(
3615         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3616        );
3617     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3618     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3621 //########################
3622 //##       Spiral       ##
3623 //########################
3625 static void
3626 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3628     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3630     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3631         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3632         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3633     }
3635     // quit if run by the attr_changed listener
3636     if (g_object_get_data( tbl, "freeze" )) {
3637         return;
3638     }
3640     // in turn, prevent listener from responding
3641     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3643     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3645     bool modmade = false;
3646     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3647          items != NULL;
3648          items = items->next)
3649     {
3650         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3651             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3652             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3653             SP_OBJECT((SPItem *) items->data)->updateRepr();
3654             modmade = true;
3655         }
3656     }
3658     g_free(namespaced_name);
3660     if (modmade) {
3661         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3662                                    _("Change spiral"));
3663     }
3665     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3668 static void
3669 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3671     sp_spl_tb_value_changed(adj, tbl, "revolution");
3674 static void
3675 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3677     sp_spl_tb_value_changed(adj, tbl, "expansion");
3680 static void
3681 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3683     sp_spl_tb_value_changed(adj, tbl, "t0");
3686 static void
3687 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3689     GtkWidget *tbl = GTK_WIDGET(obj);
3691     GtkAdjustment *adj;
3693     // fixme: make settable
3694     gdouble rev = 5;
3695     gdouble exp = 1.0;
3696     gdouble t0 = 0.0;
3698     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3699     gtk_adjustment_set_value(adj, rev);
3700     gtk_adjustment_value_changed(adj);
3702     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3703     gtk_adjustment_set_value(adj, exp);
3704     gtk_adjustment_value_changed(adj);
3706     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3707     gtk_adjustment_set_value(adj, t0);
3708     gtk_adjustment_value_changed(adj);
3710     spinbutton_defocus(GTK_OBJECT(tbl));
3714 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3715                                          gchar const */*old_value*/, gchar const */*new_value*/,
3716                                          bool /*is_interactive*/, gpointer data)
3718     GtkWidget *tbl = GTK_WIDGET(data);
3720     // quit if run by the _changed callbacks
3721     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3722         return;
3723     }
3725     // in turn, prevent callbacks from responding
3726     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3728     GtkAdjustment *adj;
3729     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3730     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3732     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3733     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3735     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3736     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3738     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3742 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3743     NULL, /* child_added */
3744     NULL, /* child_removed */
3745     spiral_tb_event_attr_changed,
3746     NULL, /* content_changed */
3747     NULL  /* order_changed */
3748 };
3750 static void
3751 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3753     int n_selected = 0;
3754     Inkscape::XML::Node *repr = NULL;
3756     purge_repr_listener( tbl, tbl );
3758     for (GSList const *items = selection->itemList();
3759          items != NULL;
3760          items = items->next)
3761     {
3762         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3763             n_selected++;
3764             repr = SP_OBJECT_REPR((SPItem *) items->data);
3765         }
3766     }
3768     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3770     if (n_selected == 0) {
3771         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3772     } else if (n_selected == 1) {
3773         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3775         if (repr) {
3776             g_object_set_data( tbl, "repr", repr );
3777             Inkscape::GC::anchor(repr);
3778             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3779             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3780         }
3781     } else {
3782         // FIXME: implement averaging of all parameters for multiple selected
3783         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3784         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3785     }
3789 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3791     EgeAdjustmentAction* eact = 0;
3792     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3794     {
3795         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3796         ege_output_action_set_use_markup( act, TRUE );
3797         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3798         g_object_set_data( holder, "mode_action", act );
3799     }
3801     /* Revolution */
3802     {
3803         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3804         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3805         eact = create_adjustment_action( "SpiralRevolutionAction",
3806                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3807                                          "/tools/shapes/spiral/revolution", 3.0,
3808                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3809                                          0.01, 1024.0, 0.1, 1.0,
3810                                          labels, values, G_N_ELEMENTS(labels),
3811                                          sp_spl_tb_revolution_value_changed, 1, 2);
3812         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3813     }
3815     /* Expansion */
3816     {
3817         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3818         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3819         eact = create_adjustment_action( "SpiralExpansionAction",
3820                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3821                                          "/tools/shapes/spiral/expansion", 1.0,
3822                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3823                                          0.0, 1000.0, 0.01, 1.0,
3824                                          labels, values, G_N_ELEMENTS(labels),
3825                                          sp_spl_tb_expansion_value_changed);
3826         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3827     }
3829     /* T0 */
3830     {
3831         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3832         gdouble values[] = {0, 0.5, 0.9};
3833         eact = create_adjustment_action( "SpiralT0Action",
3834                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3835                                          "/tools/shapes/spiral/t0", 0.0,
3836                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3837                                          0.0, 0.999, 0.01, 1.0,
3838                                          labels, values, G_N_ELEMENTS(labels),
3839                                          sp_spl_tb_t0_value_changed);
3840         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3841     }
3843     /* Reset */
3844     {
3845         InkAction* inky = ink_action_new( "SpiralResetAction",
3846                                           _("Defaults"),
3847                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3848                                           GTK_STOCK_CLEAR,
3849                                           secondarySize );
3850         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3851         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3852     }
3855     sigc::connection *connection = new sigc::connection(
3856         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3857         );
3858     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3859     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3862 //########################
3863 //##     Pen/Pencil     ##
3864 //########################
3866 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3867 static Glib::ustring const
3868 freehand_tool_name(GObject *dataKludge)
3870     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3871     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3872              ? "/tools/freehand/pen"
3873              : "/tools/freehand/pencil" );
3876 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3878     gint mode = ege_select_one_action_get_active(act);
3880     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3881     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3883     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3885     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3886     // preparatory work here
3887     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3888         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3889         sp_pen_context_set_polyline_mode(pc);
3890     }
3893 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3895     /* Freehand mode toggle buttons */
3896     {
3897         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3898         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3899         Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3901         {
3902             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3904             GtkTreeIter iter;
3905             gtk_list_store_append( model, &iter );
3906             gtk_list_store_set( model, &iter,
3907                                 0, _("Bezier"),
3908                                 1, _("Create regular Bezier path"),
3909                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3910                                 -1 );
3912             gtk_list_store_append( model, &iter );
3913             gtk_list_store_set( model, &iter,
3914                                 0, _("Spiro"),
3915                                 1, _("Create Spiro path"),
3916                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3917                                 -1 );
3919             if (!tool_is_pencil) {
3920                 gtk_list_store_append( model, &iter );
3921                 gtk_list_store_set( model, &iter,
3922                                     0, _("Zigzag"),
3923                                     1, _("Create a sequence of straight line segments"),
3924                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3925                                     -1 );
3927                 gtk_list_store_append( model, &iter );
3928                 gtk_list_store_set( model, &iter,
3929                                     0, _("Paraxial"),
3930                                     1, _("Create a sequence of paraxial line segments"),
3931                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3932                                     -1 );
3933             }
3935             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3936                                                                 "FreehandModeActionPencil" :
3937                                                                 "FreehandModeActionPen",
3938                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3939             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3941             ege_select_one_action_set_appearance( act, "full" );
3942             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3943             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3944             ege_select_one_action_set_icon_column( act, 2 );
3945             ege_select_one_action_set_icon_size( act, secondarySize );
3946             ege_select_one_action_set_tooltip_column( act, 1  );
3948             ege_select_one_action_set_active( act, freehandMode);
3949             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3950         }
3951     }
3954 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3955     gint shape = ege_select_one_action_get_active( act );
3956     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3957     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3960 /**
3961  * \brief Generate the list of freehand advanced shape option entries.
3962  */
3963 GList * freehand_shape_dropdown_items_list() {
3964     GList *glist = NULL;
3966     glist = g_list_append (glist, _("None"));
3967     glist = g_list_append (glist, _("Triangle in"));
3968     glist = g_list_append (glist, _("Triangle out"));
3969     glist = g_list_append (glist, _("Ellipse"));
3970     glist = g_list_append (glist, _("From clipboard"));
3972     return glist;
3975 static void
3976 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3977     /*advanced shape options */
3978     {
3979         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3980         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3982         GList* items = 0;
3983         gint count = 0;
3984         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3985         {
3986             GtkTreeIter iter;
3987             gtk_list_store_append( model, &iter );
3988             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3989             count++;
3990         }
3991         g_list_free( items );
3992         items = 0;
3993         EgeSelectOneAction* act1 = ege_select_one_action_new(
3994             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3995             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3996         g_object_set( act1, "short_label", _("Shape:"), NULL );
3997         ege_select_one_action_set_appearance( act1, "compact" );
3998         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3999         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
4000         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
4001         g_object_set_data( holder, "shape_action", act1 );
4002     }
4005 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
4007     sp_add_freehand_mode_toggle(mainActions, holder, false);
4008     freehand_add_advanced_shape_options(mainActions, holder, false);
4012 static void
4013 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
4015     GtkWidget *tbl = GTK_WIDGET(obj);
4017     GtkAdjustment *adj;
4019     // fixme: make settable
4020     gdouble tolerance = 4;
4022     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
4023     gtk_adjustment_set_value(adj, tolerance);
4024     gtk_adjustment_value_changed(adj);
4026     spinbutton_defocus(GTK_OBJECT(tbl));
4029 static void
4030 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
4032     // quit if run by the attr_changed listener
4033     if (g_object_get_data( tbl, "freeze" )) {
4034         return;
4035     }
4036     // in turn, prevent listener from responding
4037     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4038     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4039     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
4040     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4043 /*
4044 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
4045 public:
4046     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
4047     {
4048         g_object_set_data(_obj, "prefobserver", this);
4049     }
4050     virtual ~PencilToleranceObserver() {
4051         if (g_object_get_data(_obj, "prefobserver") == this) {
4052             g_object_set_data(_obj, "prefobserver", NULL);
4053         }
4054     }
4055     virtual void notify(Inkscape::Preferences::Entry const &val) {
4056         GObject* tbl = _obj;
4057         if (g_object_get_data( tbl, "freeze" )) {
4058             return;
4059         }
4060         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4062         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
4064         double v = val.getDouble(adj->value);
4065         gtk_adjustment_set_value(adj, v);
4066         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4067     }
4068 private:
4069     GObject *_obj;
4070 };
4071 */
4073 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4075     sp_add_freehand_mode_toggle(mainActions, holder, true);
4077     EgeAdjustmentAction* eact = 0;
4079     /* Tolerance */
4080     {
4081         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4082         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4083         eact = create_adjustment_action( "PencilToleranceAction",
4084                                          _("Smoothing:"), _("Smoothing: "),
4085                  _("How much smoothing (simplifying) is applied to the line"),
4086                                          "/tools/freehand/pencil/tolerance",
4087                                          3.0,
4088                                          GTK_WIDGET(desktop->canvas), NULL,
4089                                          holder, TRUE, "altx-pencil",
4090                                          1, 100.0, 0.5, 1.0,
4091                                          labels, values, G_N_ELEMENTS(labels),
4092                                          sp_pencil_tb_tolerance_value_changed,
4093                                          1, 2);
4094         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4095         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4096     }
4098     /* advanced shape options */
4099     freehand_add_advanced_shape_options(mainActions, holder, true);
4101     /* Reset */
4102     {
4103         InkAction* inky = ink_action_new( "PencilResetAction",
4104                                           _("Defaults"),
4105                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4106                                           GTK_STOCK_CLEAR,
4107                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4108         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4109         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4110     }
4112     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4117 //########################
4118 //##       Tweak        ##
4119 //########################
4121 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4123     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4124     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4127 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4129     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4130     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4133 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4135     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4136     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4139 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4141     int mode = ege_select_one_action_get_active( act );
4142     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4143     prefs->setInt("/tools/tweak/mode", mode);
4145     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4146     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4147     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4148     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4149     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4150     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4151     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4152         if (doh) gtk_action_set_sensitive (doh, TRUE);
4153         if (dos) gtk_action_set_sensitive (dos, TRUE);
4154         if (dol) gtk_action_set_sensitive (dol, TRUE);
4155         if (doo) gtk_action_set_sensitive (doo, TRUE);
4156         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4157         if (fid) gtk_action_set_sensitive (fid, FALSE);
4158     } else {
4159         if (doh) gtk_action_set_sensitive (doh, FALSE);
4160         if (dos) gtk_action_set_sensitive (dos, FALSE);
4161         if (dol) gtk_action_set_sensitive (dol, FALSE);
4162         if (doo) gtk_action_set_sensitive (doo, FALSE);
4163         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4164         if (fid) gtk_action_set_sensitive (fid, TRUE);
4165     }
4168 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4170     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4171     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4174 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4175     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4176     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4178 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4179     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4180     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4182 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4183     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4184     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4186 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4187     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4188     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4191 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4193     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4194     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4196     {
4197         /* Width */
4198         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4199         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4200         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4201                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4202                                                               "/tools/tweak/width", 15,
4203                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4204                                                               1, 100, 1.0, 10.0,
4205                                                               labels, values, G_N_ELEMENTS(labels),
4206                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4207         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4208         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4209         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4210     }
4213     {
4214         /* Force */
4215         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4216         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4217         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4218                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4219                                                               "/tools/tweak/force", 20,
4220                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4221                                                               1, 100, 1.0, 10.0,
4222                                                               labels, values, G_N_ELEMENTS(labels),
4223                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4224         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4225         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4226         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4227     }
4229     /* Mode */
4230     {
4231         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4233         GtkTreeIter iter;
4234         gtk_list_store_append( model, &iter );
4235         gtk_list_store_set( model, &iter,
4236                             0, _("Move mode"),
4237                             1, _("Move objects in any direction"),
4238                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4239                             -1 );
4241         gtk_list_store_append( model, &iter );
4242         gtk_list_store_set( model, &iter,
4243                             0, _("Move in/out mode"),
4244                             1, _("Move objects towards cursor; with Shift from cursor"),
4245                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4246                             -1 );
4248         gtk_list_store_append( model, &iter );
4249         gtk_list_store_set( model, &iter,
4250                             0, _("Move jitter mode"),
4251                             1, _("Move objects in random directions"),
4252                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4253                             -1 );
4255         gtk_list_store_append( model, &iter );
4256         gtk_list_store_set( model, &iter,
4257                             0, _("Scale mode"),
4258                             1, _("Shrink objects, with Shift enlarge"),
4259                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4260                             -1 );
4262         gtk_list_store_append( model, &iter );
4263         gtk_list_store_set( model, &iter,
4264                             0, _("Rotate mode"),
4265                             1, _("Rotate objects, with Shift counterclockwise"),
4266                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4267                             -1 );
4269         gtk_list_store_append( model, &iter );
4270         gtk_list_store_set( model, &iter,
4271                             0, _("Duplicate/delete mode"),
4272                             1, _("Duplicate objects, with Shift delete"),
4273                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4274                             -1 );
4276         gtk_list_store_append( model, &iter );
4277         gtk_list_store_set( model, &iter,
4278                             0, _("Push mode"),
4279                             1, _("Push parts of paths in any direction"),
4280                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4281                             -1 );
4283         gtk_list_store_append( model, &iter );
4284         gtk_list_store_set( model, &iter,
4285                             0, _("Shrink/grow mode"),
4286                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4287                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4288                             -1 );
4290         gtk_list_store_append( model, &iter );
4291         gtk_list_store_set( model, &iter,
4292                             0, _("Attract/repel mode"),
4293                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4294                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4295                             -1 );
4297         gtk_list_store_append( model, &iter );
4298         gtk_list_store_set( model, &iter,
4299                             0, _("Roughen mode"),
4300                             1, _("Roughen parts of paths"),
4301                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4302                             -1 );
4304         gtk_list_store_append( model, &iter );
4305         gtk_list_store_set( model, &iter,
4306                             0, _("Color paint mode"),
4307                             1, _("Paint the tool's color upon selected objects"),
4308                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4309                             -1 );
4311         gtk_list_store_append( model, &iter );
4312         gtk_list_store_set( model, &iter,
4313                             0, _("Color jitter mode"),
4314                             1, _("Jitter the colors of selected objects"),
4315                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4316                             -1 );
4318         gtk_list_store_append( model, &iter );
4319         gtk_list_store_set( model, &iter,
4320                             0, _("Blur mode"),
4321                             1, _("Blur selected objects more; with Shift, blur less"),
4322                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4323                             -1 );
4326         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4327         g_object_set( act, "short_label", _("Mode:"), NULL );
4328         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4329         g_object_set_data( holder, "mode_action", act );
4331         ege_select_one_action_set_appearance( act, "full" );
4332         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4333         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4334         ege_select_one_action_set_icon_column( act, 2 );
4335         ege_select_one_action_set_icon_size( act, secondarySize );
4336         ege_select_one_action_set_tooltip_column( act, 1  );
4338         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4339         ege_select_one_action_set_active( act, mode );
4340         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4342         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4343     }
4345     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4347     {
4348         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4349         ege_output_action_set_use_markup( act, TRUE );
4350         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4351         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4352             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4353         g_object_set_data( holder, "tweak_channels_label", act);
4354     }
4356     {
4357         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4358                                                       _("Hue"),
4359                                                       _("In color mode, act on objects' hue"),
4360                                                       NULL,
4361                                                       Inkscape::ICON_SIZE_DECORATION );
4362         //TRANSLATORS:  "H" here stands for hue
4363         g_object_set( act, "short_label", _("H"), NULL );
4364         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4365         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4366         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4367         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4368             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4369         g_object_set_data( holder, "tweak_doh", act);
4370     }
4371     {
4372         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4373                                                       _("Saturation"),
4374                                                       _("In color mode, act on objects' saturation"),
4375                                                       NULL,
4376                                                       Inkscape::ICON_SIZE_DECORATION );
4377         //TRANSLATORS: "S" here stands for Saturation
4378         g_object_set( act, "short_label", _("S"), NULL );
4379         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4380         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4381         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4382         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4383             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4384         g_object_set_data( holder, "tweak_dos", act );
4385     }
4386     {
4387         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4388                                                       _("Lightness"),
4389                                                       _("In color mode, act on objects' lightness"),
4390                                                       NULL,
4391                                                       Inkscape::ICON_SIZE_DECORATION );
4392         //TRANSLATORS: "L" here stands for Lightness
4393         g_object_set( act, "short_label", _("L"), NULL );
4394         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4395         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4396         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4397         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4398             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4399         g_object_set_data( holder, "tweak_dol", act );
4400     }
4401     {
4402         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4403                                                       _("Opacity"),
4404                                                       _("In color mode, act on objects' opacity"),
4405                                                       NULL,
4406                                                       Inkscape::ICON_SIZE_DECORATION );
4407         //TRANSLATORS: "O" here stands for Opacity
4408         g_object_set( act, "short_label", _("O"), NULL );
4409         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4410         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4411         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4412         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4413             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4414         g_object_set_data( holder, "tweak_doo", act );
4415     }
4417     {   /* Fidelity */
4418         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4419         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4420         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4421                                                               _("Fidelity"), _("Fidelity:"),
4422                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4423                                                               "/tools/tweak/fidelity", 50,
4424                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4425                                                               1, 100, 1.0, 10.0,
4426                                                               labels, values, G_N_ELEMENTS(labels),
4427                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4428         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4429         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4430         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4431             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4432         g_object_set_data( holder, "tweak_fidelity", eact );
4433     }
4436     /* Use Pressure button */
4437     {
4438         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4439                                                       _("Pressure"),
4440                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4441                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4442                                                       Inkscape::ICON_SIZE_DECORATION );
4443         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4444         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4445         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4446     }
4451 //########################
4452 //##       Spray        ##
4453 //########################
4455 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4457     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458     prefs->setDouble( "/tools/spray/width", adj->value );
4461 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4463     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4464     prefs->setDouble( "/tools/spray/mean", adj->value );
4467 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4469     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4470     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4473 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4475     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4476     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4479 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4481     int mode = ege_select_one_action_get_active( act );
4482     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4483     prefs->setInt("/tools/spray/mode", mode);
4486 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4488     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4489     prefs->setDouble( "/tools/spray/population", adj->value );
4492 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4494     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4495     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4498 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4500     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4501     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4505 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4507     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4508     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4510     {
4511         /* Width */
4512         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4513         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4514         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4515                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4516                                                               "/tools/spray/width", 15,
4517                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4518                                                               1, 100, 1.0, 10.0,
4519                                                               labels, values, G_N_ELEMENTS(labels),
4520                                                               sp_spray_width_value_changed,  1, 0 );
4521         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4522         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4523         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4524     }
4526     {
4527         /* Mean */
4528         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4529         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4530         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4531                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4532                                                               "/tools/spray/mean", 0,
4533                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4534                                                               0, 100, 1.0, 10.0,
4535                                                               labels, values, G_N_ELEMENTS(labels),
4536                                                               sp_spray_mean_value_changed,  1, 0 );
4537         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4538         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4539         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4540     }
4542     {
4543         /* Standard_deviation */
4544         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4545         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4546         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4547                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4548                                                               "/tools/spray/standard_deviation", 70,
4549                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4550                                                               1, 100, 1.0, 10.0,
4551                                                               labels, values, G_N_ELEMENTS(labels),
4552                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4553         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4554         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4555         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4556     }
4558     /* Mode */
4559     {
4560         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4562         GtkTreeIter iter;
4563         gtk_list_store_append( model, &iter );
4564         gtk_list_store_set( model, &iter,
4565                             0, _("Spray with copies"),
4566                             1, _("Spray copies of the initial selection"),
4567                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4568                             -1 );
4570         gtk_list_store_append( model, &iter );
4571         gtk_list_store_set( model, &iter,
4572                             0, _("Spray with clones"),
4573                             1, _("Spray clones of the initial selection"),
4574                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4575                             -1 );
4577         gtk_list_store_append( model, &iter );
4578         gtk_list_store_set( model, &iter,
4579                             0, _("Spray single path"),
4580                             1, _("Spray objects in a single path"),
4581                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4582                             -1 );
4584         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4585         g_object_set( act, "short_label", _("Mode:"), NULL );
4586         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4587         g_object_set_data( holder, "mode_action", act );
4589         ege_select_one_action_set_appearance( act, "full" );
4590         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4591         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4592         ege_select_one_action_set_icon_column( act, 2 );
4593         ege_select_one_action_set_icon_size( act, secondarySize );
4594         ege_select_one_action_set_tooltip_column( act, 1  );
4596         gint mode = prefs->getInt("/tools/spray/mode", 1);
4597         ege_select_one_action_set_active( act, mode );
4598         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4600         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4601     }
4603     {   /* Population */
4604         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4605         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4606         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4607                                                               _("Amount"), _("Amount:"),
4608                                                               _("Adjusts the number of items sprayed per clic."),
4609                                                               "/tools/spray/population", 70,
4610                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4611                                                               1, 100, 1.0, 10.0,
4612                                                               labels, values, G_N_ELEMENTS(labels),
4613                                                               sp_spray_population_value_changed,  1, 0 );
4614         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4615         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4616         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4617         g_object_set_data( holder, "spray_population", eact );
4618     }
4620     /* Use Pressure button */
4621     {
4622         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4623                                                       _("Pressure"),
4624                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4625                                                       "use_pressure",
4626                                                       Inkscape::ICON_SIZE_DECORATION );
4627         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4628         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4629         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4630     }
4632     {   /* Rotation */
4633         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4634         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4635         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4636                                                               _("Rotation"), _("Rotation:"),
4637                                                               // xgettext:no-c-format
4638                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4639                                                               "/tools/spray/rotation_variation", 0,
4640                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4641                                                               0, 100, 1.0, 10.0,
4642                                                               labels, values, G_N_ELEMENTS(labels),
4643                                                               sp_spray_rotation_value_changed,  1, 0 );
4644         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4645         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4646         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4647         g_object_set_data( holder, "spray_rotation", eact );
4648     }
4650     {   /* Scale */
4651         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4652         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4653         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4654                                                               _("Scale"), _("Scale:"),
4655                                                               // xgettext:no-c-format
4656                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4657                                                               "/tools/spray/scale_variation", 0,
4658                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4659                                                               0, 100, 1.0, 10.0,
4660                                                               labels, values, G_N_ELEMENTS(labels),
4661                                                               sp_spray_scale_value_changed,  1, 0 );
4662         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4663         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4664         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4665         g_object_set_data( holder, "spray_scale", eact );
4666     }
4673 //########################
4674 //##     Calligraphy    ##
4675 //########################
4676 static void update_presets_list (GObject *tbl)
4678     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4679     if (g_object_get_data(tbl, "presets_blocked"))
4680         return;
4682     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4683     if (!sel) {
4684         // WTF!? This will cause a segfault if ever reached
4685         //ege_select_one_action_set_active(sel, 0);
4686         return;
4687     }
4689     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4691     int ege_index = 1;
4692     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4693         bool match = true;
4695         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4696         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4697             Glib::ustring entry_name = j->getEntryName();
4698             if (entry_name == "id" || entry_name == "name") continue;
4700             void *widget = g_object_get_data(tbl, entry_name.data());
4701             if (widget) {
4702                 if (GTK_IS_ADJUSTMENT(widget)) {
4703                     double v = j->getDouble();
4704                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4705                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4706                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4707                         match = false;
4708                         break;
4709                     }
4710                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4711                     bool v = j->getBool();
4712                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4713                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4714                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4715                         match = false;
4716                         break;
4717                     }
4718                 }
4719             }
4720         }
4722         if (match) {
4723             // newly added item is at the same index as the
4724             // save command, so we need to change twice for it to take effect
4725             ege_select_one_action_set_active(sel, 0);
4726             ege_select_one_action_set_active(sel, ege_index); // one-based index
4727             return;
4728         }
4729     }
4731     // no match found
4732     ege_select_one_action_set_active(sel, 0);
4735 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4737     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4738     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4739     update_presets_list(tbl);
4742 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4744     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4745     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4746     update_presets_list(tbl);
4749 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4751     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4752     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4753     update_presets_list(tbl);
4756 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4758     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4759     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4760     update_presets_list(tbl);
4763 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4765     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4766     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4767     update_presets_list(tbl);
4770 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4772     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4773     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4774     update_presets_list(tbl);
4777 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4779     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4780     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4781     update_presets_list(tbl);
4784 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4786     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4787     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4788     update_presets_list(tbl);
4791 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4793     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4794     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4795     update_presets_list(tbl);
4798 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4800     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4801     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4802     update_presets_list(tbl);
4805 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4807     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4808     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4809     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4810     update_presets_list(tbl);
4811     if (calligraphy_angle )
4812         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4816 static gchar const *const widget_names[] = {
4817     "width",
4818     "mass",
4819     "wiggle",
4820     "angle",
4821     "thinning",
4822     "tremor",
4823     "flatness",
4824     "cap_rounding",
4825     "usepressure",
4826     "tracebackground",
4827     "usetilt"
4828 };
4831 static void sp_dcc_build_presets_list(GObject *tbl)
4833     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4835     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4836     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4837     gtk_list_store_clear (model);
4839     {
4840         GtkTreeIter iter;
4841         gtk_list_store_append( model, &iter );
4842         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4843     }
4845     // iterate over all presets to populate the list
4846     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4847     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4848     int ii=1;
4850     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4851         GtkTreeIter iter;
4852         Glib::ustring preset_name = prefs->getString(*i + "/name");
4853         gtk_list_store_append( model, &iter );
4854         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4855     }
4857     {
4858         GtkTreeIter iter;
4859         gtk_list_store_append( model, &iter );
4860         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4861         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4862     }
4864     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4866     update_presets_list (tbl);
4869 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4871     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4872     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4873     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4874     if (! desktop) return;
4876     if (g_object_get_data(tbl, "presets_blocked"))
4877         return;
4879     CalligraphicProfileRename::show(desktop);
4880     if ( !CalligraphicProfileRename::applied()) {
4881         // dialog cancelled
4882         update_presets_list (tbl);
4883         return;
4884     }
4885     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4887     if (profile_name.empty()) {
4888         // empty name entered
4889         update_presets_list (tbl);
4890         return;
4891     }
4893     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4895     // If there's a preset with the given name, find it and set save_path appropriately
4896     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4897     int total_presets = presets.size();
4898     int new_index = -1;
4899     Glib::ustring save_path; // profile pref path without a trailing slash
4901     int temp_index = 0;
4902     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4903         Glib::ustring name = prefs->getString(*i + "/name");
4904         if (!name.empty() && profile_name == name) {
4905             new_index = temp_index;
4906             save_path = *i;
4907             break;
4908         }
4909     }
4911     if (new_index == -1) {
4912         // no preset with this name, create
4913         new_index = total_presets + 1;
4914         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4915         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4916         g_free(profile_id);
4917     }
4919     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4920         gchar const *const widget_name = widget_names[i];
4921         void *widget = g_object_get_data(tbl, widget_name);
4922         if (widget) {
4923             if (GTK_IS_ADJUSTMENT(widget)) {
4924                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4925                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4926                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4927             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4928                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4929                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4930                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4931             } else {
4932                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4933             }
4934         } else {
4935             g_warning("Bad key when writing preset: %s\n", widget_name);
4936         }
4937     }
4938     prefs->setString(save_path + "/name", profile_name);
4940     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4941     sp_dcc_build_presets_list (tbl);
4945 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4947     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4949     gint preset_index = ege_select_one_action_get_active( act );
4950     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4951     // even when the preset is not changed. It would be good to replace it with something more
4952     // modern. Index 0 means "No preset", so we don't do anything.
4953     if (preset_index == 0) return;
4955     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4957     if (preset_index == save_presets_index) {
4958         // this is the Save command
4959         sp_dcc_save_profile(NULL, tbl);
4960         return;
4961     }
4963     if (g_object_get_data(tbl, "presets_blocked"))
4964         return;
4966     // preset_index is one-based so we subtract 1
4967     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4968     Glib::ustring preset_path = presets.at(preset_index - 1);
4970     if (!preset_path.empty()) {
4971         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
4973         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4975         // Shouldn't this be std::map?
4976         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4977             Glib::ustring entry_name = i->getEntryName();
4978             if (entry_name == "id" || entry_name == "name") continue;
4979             void *widget = g_object_get_data(tbl, entry_name.data());
4980             if (widget) {
4981                 if (GTK_IS_ADJUSTMENT(widget)) {
4982                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4983                     gtk_adjustment_set_value(adj, i->getDouble());
4984                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4985                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4986                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4987                     gtk_toggle_action_set_active(toggle, i->getBool());
4988                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4989                 } else {
4990                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4991                 }
4992             } else {
4993                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4994             }
4995         }
4996         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4997     }
5001 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5003     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5004     {
5005         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
5007         EgeAdjustmentAction* calligraphy_angle = 0;
5009         {
5010         /* Width */
5011         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5012         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5013         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
5014                                                               _("Pen Width"), _("Width:"),
5015                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
5016                                                               "/tools/calligraphic/width", 15,
5017                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
5018                                                               1, 100, 1.0, 10.0,
5019                                                               labels, values, G_N_ELEMENTS(labels),
5020                                                               sp_ddc_width_value_changed,  1, 0 );
5021         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5022         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5023         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5024         }
5026         {
5027         /* Thinning */
5028             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
5029             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
5030         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
5031                                                               _("Stroke Thinning"), _("Thinning:"),
5032                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
5033                                                               "/tools/calligraphic/thinning", 10,
5034                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5035                                                               -100, 100, 1, 10.0,
5036                                                               labels, values, G_N_ELEMENTS(labels),
5037                                                               sp_ddc_velthin_value_changed, 1, 0);
5038         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5039         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5040         }
5042         {
5043         /* Angle */
5044         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
5045         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
5046         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
5047                                                               _("Pen Angle"), _("Angle:"),
5048                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
5049                                                               "/tools/calligraphic/angle", 30,
5050                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
5051                                                               -90.0, 90.0, 1.0, 10.0,
5052                                                               labels, values, G_N_ELEMENTS(labels),
5053                                                               sp_ddc_angle_value_changed, 1, 0 );
5054         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5055         g_object_set_data( holder, "angle_action", eact );
5056         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5057         calligraphy_angle = eact;
5058         }
5060         {
5061         /* Fixation */
5062             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
5063         gdouble values[] = {0, 20, 40, 60, 90, 100};
5064         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
5065                                                               _("Fixation"), _("Fixation:"),
5066                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
5067                                                               "/tools/calligraphic/flatness", 90,
5068                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5069                                                               0.0, 100, 1.0, 10.0,
5070                                                               labels, values, G_N_ELEMENTS(labels),
5071                                                               sp_ddc_flatness_value_changed, 1, 0);
5072         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5073         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5074         }
5076         {
5077         /* Cap Rounding */
5078             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
5079         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
5080         // TRANSLATORS: "cap" means "end" (both start and finish) here
5081         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
5082                                                               _("Cap rounding"), _("Caps:"),
5083                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
5084                                                               "/tools/calligraphic/cap_rounding", 0.0,
5085                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5086                                                               0.0, 5.0, 0.01, 0.1,
5087                                                               labels, values, G_N_ELEMENTS(labels),
5088                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
5089         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5090         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5091         }
5093         {
5094         /* Tremor */
5095             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
5096         gdouble values[] = {0, 10, 20, 40, 60, 100};
5097         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
5098                                                               _("Stroke Tremor"), _("Tremor:"),
5099                                                               _("Increase to make strokes rugged and trembling"),
5100                                                               "/tools/calligraphic/tremor", 0.0,
5101                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5102                                                               0.0, 100, 1, 10.0,
5103                                                               labels, values, G_N_ELEMENTS(labels),
5104                                                               sp_ddc_tremor_value_changed, 1, 0);
5106         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5107         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5108         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5109         }
5111         {
5112         /* Wiggle */
5113         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5114         gdouble values[] = {0, 20, 40, 60, 100};
5115         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5116                                                               _("Pen Wiggle"), _("Wiggle:"),
5117                                                               _("Increase to make the pen waver and wiggle"),
5118                                                               "/tools/calligraphic/wiggle", 0.0,
5119                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5120                                                               0.0, 100, 1, 10.0,
5121                                                               labels, values, G_N_ELEMENTS(labels),
5122                                                               sp_ddc_wiggle_value_changed, 1, 0);
5123         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5124         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5125         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5126         }
5128         {
5129         /* Mass */
5130             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5131         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5132         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5133                                                               _("Pen Mass"), _("Mass:"),
5134                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5135                                                               "/tools/calligraphic/mass", 2.0,
5136                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5137                                                               0.0, 100, 1, 10.0,
5138                                                               labels, values, G_N_ELEMENTS(labels),
5139                                                               sp_ddc_mass_value_changed, 1, 0);
5140         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5141         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5142         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5143         }
5146         /* Trace Background button */
5147         {
5148             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5149                                                           _("Trace Background"),
5150                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5151                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5152                                                           Inkscape::ICON_SIZE_DECORATION );
5153             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5154             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5155             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5156             g_object_set_data( holder, "tracebackground", act );
5157         }
5159         /* Use Pressure button */
5160         {
5161             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5162                                                           _("Pressure"),
5163                                                           _("Use the pressure of the input device to alter the width of the pen"),
5164                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5165                                                           Inkscape::ICON_SIZE_DECORATION );
5166             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5167             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5168             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5169             g_object_set_data( holder, "usepressure", act );
5170         }
5172         /* Use Tilt button */
5173         {
5174             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5175                                                           _("Tilt"),
5176                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5177                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5178                                                           Inkscape::ICON_SIZE_DECORATION );
5179             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5180             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5181             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5182             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5183             g_object_set_data( holder, "usetilt", act );
5184         }
5186         /*calligraphic profile */
5187         {
5188             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5189             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5190             ege_select_one_action_set_appearance (act1, "compact");
5191             g_object_set_data (holder, "profile_selector", act1 );
5193             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5195             sp_dcc_build_presets_list (holder);
5197             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5198             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5199         }
5200     }
5204 //########################
5205 //##    Circle / Arc    ##
5206 //########################
5208 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5210     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5211     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5213     if (v1 == 0 && v2 == 0) {
5214         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5215             gtk_action_set_sensitive( ocb, FALSE );
5216             gtk_action_set_sensitive( make_whole, FALSE );
5217         }
5218     } else {
5219         gtk_action_set_sensitive( ocb, TRUE );
5220         gtk_action_set_sensitive( make_whole, TRUE );
5221     }
5224 static void
5225 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5227     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5229     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5230         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5231         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5232     }
5234     // quit if run by the attr_changed listener
5235     if (g_object_get_data( tbl, "freeze" )) {
5236         return;
5237     }
5239     // in turn, prevent listener from responding
5240     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5242     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5244     bool modmade = false;
5245     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5246          items != NULL;
5247          items = items->next)
5248     {
5249         SPItem *item = SP_ITEM(items->data);
5251         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5253             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5254             SPArc *arc = SP_ARC(item);
5256             if (!strcmp(value_name, "start"))
5257                 ge->start = (adj->value * M_PI)/ 180;
5258             else
5259                 ge->end = (adj->value * M_PI)/ 180;
5261             sp_genericellipse_normalize(ge);
5262             ((SPObject *)arc)->updateRepr();
5263             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5265             modmade = true;
5266         }
5267     }
5269     g_free(namespaced_name);
5271     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5273     sp_arctb_sensitivize( tbl, adj->value, other->value );
5275     if (modmade) {
5276         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5277                                    _("Arc: Change start/end"));
5278     }
5280     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5284 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5286     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5289 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5291     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5295 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5297     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5298     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5299         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5300         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5301     }
5303     // quit if run by the attr_changed listener
5304     if (g_object_get_data( tbl, "freeze" )) {
5305         return;
5306     }
5308     // in turn, prevent listener from responding
5309     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5311     bool modmade = false;
5313     if ( ege_select_one_action_get_active(act) != 0 ) {
5314         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5315              items != NULL;
5316              items = items->next)
5317         {
5318             if (SP_IS_ARC((SPItem *) items->data)) {
5319                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5320                 repr->setAttribute("sodipodi:open", "true");
5321                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5322                 modmade = true;
5323             }
5324         }
5325     } else {
5326         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5327              items != NULL;
5328              items = items->next)
5329         {
5330             if (SP_IS_ARC((SPItem *) items->data))    {
5331                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5332                 repr->setAttribute("sodipodi:open", NULL);
5333                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5334                 modmade = true;
5335             }
5336         }
5337     }
5339     if (modmade) {
5340         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5341                                    _("Arc: Change open/closed"));
5342     }
5344     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5347 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5349     GtkAdjustment *adj;
5350     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5351     gtk_adjustment_set_value(adj, 0.0);
5352     gtk_adjustment_value_changed(adj);
5354     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5355     gtk_adjustment_set_value(adj, 0.0);
5356     gtk_adjustment_value_changed(adj);
5358     spinbutton_defocus( GTK_OBJECT(obj) );
5361 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5362                                       gchar const */*old_value*/, gchar const */*new_value*/,
5363                                       bool /*is_interactive*/, gpointer data)
5365     GObject *tbl = G_OBJECT(data);
5367     // quit if run by the _changed callbacks
5368     if (g_object_get_data( tbl, "freeze" )) {
5369         return;
5370     }
5372     // in turn, prevent callbacks from responding
5373     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5375     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5376     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5378     GtkAdjustment *adj1,*adj2;
5379     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5380     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5381     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5382     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5384     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5386     char const *openstr = NULL;
5387     openstr = repr->attribute("sodipodi:open");
5388     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5390     if (openstr) {
5391         ege_select_one_action_set_active( ocb, 1 );
5392     } else {
5393         ege_select_one_action_set_active( ocb, 0 );
5394     }
5396     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5399 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5400     NULL, /* child_added */
5401     NULL, /* child_removed */
5402     arc_tb_event_attr_changed,
5403     NULL, /* content_changed */
5404     NULL  /* order_changed */
5405 };
5408 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5410     int n_selected = 0;
5411     Inkscape::XML::Node *repr = NULL;
5413     purge_repr_listener( tbl, tbl );
5415     for (GSList const *items = selection->itemList();
5416          items != NULL;
5417          items = items->next)
5418     {
5419         if (SP_IS_ARC((SPItem *) items->data)) {
5420             n_selected++;
5421             repr = SP_OBJECT_REPR((SPItem *) items->data);
5422         }
5423     }
5425     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5427     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5428     if (n_selected == 0) {
5429         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5430     } else if (n_selected == 1) {
5431         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5432         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5434         if (repr) {
5435             g_object_set_data( tbl, "repr", repr );
5436             Inkscape::GC::anchor(repr);
5437             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5438             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5439         }
5440     } else {
5441         // FIXME: implement averaging of all parameters for multiple selected
5442         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5443         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5444         sp_arctb_sensitivize( tbl, 1, 0 );
5445     }
5449 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5451     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5453     EgeAdjustmentAction* eact = 0;
5454     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5457     {
5458         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5459         ege_output_action_set_use_markup( act, TRUE );
5460         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5461         g_object_set_data( holder, "mode_action", act );
5462     }
5464     /* Start */
5465     {
5466         eact = create_adjustment_action( "ArcStartAction",
5467                                          _("Start"), _("Start:"),
5468                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5469                                          "/tools/shapes/arc/start", 0.0,
5470                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5471                                          -360.0, 360.0, 1.0, 10.0,
5472                                          0, 0, 0,
5473                                          sp_arctb_start_value_changed);
5474         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5475     }
5477     /* End */
5478     {
5479         eact = create_adjustment_action( "ArcEndAction",
5480                                          _("End"), _("End:"),
5481                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5482                                          "/tools/shapes/arc/end", 0.0,
5483                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5484                                          -360.0, 360.0, 1.0, 10.0,
5485                                          0, 0, 0,
5486                                          sp_arctb_end_value_changed);
5487         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5488     }
5490     /* Segments / Pie checkbox */
5491     {
5492         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5494         GtkTreeIter iter;
5495         gtk_list_store_append( model, &iter );
5496         gtk_list_store_set( model, &iter,
5497                             0, _("Closed arc"),
5498                             1, _("Switch to segment (closed shape with two radii)"),
5499                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5500                             -1 );
5502         gtk_list_store_append( model, &iter );
5503         gtk_list_store_set( model, &iter,
5504                             0, _("Open Arc"),
5505                             1, _("Switch to arc (unclosed shape)"),
5506                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5507                             -1 );
5509         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5510         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5511         g_object_set_data( holder, "open_action", act );
5513         ege_select_one_action_set_appearance( act, "full" );
5514         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5515         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5516         ege_select_one_action_set_icon_column( act, 2 );
5517         ege_select_one_action_set_icon_size( act, secondarySize );
5518         ege_select_one_action_set_tooltip_column( act, 1  );
5520         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5521         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5522         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5523     }
5525     /* Make Whole */
5526     {
5527         InkAction* inky = ink_action_new( "ArcResetAction",
5528                                           _("Make whole"),
5529                                           _("Make the shape a whole ellipse, not arc or segment"),
5530                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5531                                           secondarySize );
5532         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5533         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5534         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5535         g_object_set_data( holder, "make_whole", inky );
5536     }
5538     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5539     // sensitivize make whole and open checkbox
5540     {
5541         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5542         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5543         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5544     }
5547     sigc::connection *connection = new sigc::connection(
5548         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5549         );
5550     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5551     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5557 // toggle button callbacks and updaters
5559 //########################
5560 //##      Dropper       ##
5561 //########################
5563 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5564     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5565     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5566     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5567     if ( set_action ) {
5568         if ( gtk_toggle_action_get_active( act ) ) {
5569             gtk_action_set_sensitive( set_action, TRUE );
5570         } else {
5571             gtk_action_set_sensitive( set_action, FALSE );
5572         }
5573     }
5575     spinbutton_defocus(GTK_OBJECT(tbl));
5578 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5579     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5580     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5581     spinbutton_defocus(GTK_OBJECT(tbl));
5585 /**
5586  * Dropper auxiliary toolbar construction and setup.
5587  *
5588  * TODO: Would like to add swatch of current color.
5589  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5590  *       can drag and drop places. Will provide a nice mixing palette.
5591  */
5592 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5594     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5595     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5597     {
5598         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5599         ege_output_action_set_use_markup( act, TRUE );
5600         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5601     }
5603     {
5604         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5605                                                       _("Pick opacity"),
5606                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5607                                                       NULL,
5608                                                       Inkscape::ICON_SIZE_DECORATION );
5609         g_object_set( act, "short_label", _("Pick"), NULL );
5610         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5611         g_object_set_data( holder, "pick_action", act );
5612         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5613         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5614     }
5616     {
5617         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5618                                                       _("Assign opacity"),
5619                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5620                                                       NULL,
5621                                                       Inkscape::ICON_SIZE_DECORATION );
5622         g_object_set( act, "short_label", _("Assign"), NULL );
5623         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5624         g_object_set_data( holder, "set_action", act );
5625         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5626         // make sure it's disabled if we're not picking alpha
5627         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5628         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5629     }
5633 //########################
5634 //##      LPETool       ##
5635 //########################
5637 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5639 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5640 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5642     using namespace Inkscape::LivePathEffect;
5644     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5645     SPEventContext *ec = desktop->event_context;
5646     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5647         return;
5648     }
5650     // only take action if run by the attr_changed listener
5651     if (!g_object_get_data(tbl, "freeze")) {
5652         // in turn, prevent listener from responding
5653         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5655         gint mode = ege_select_one_action_get_active(act);
5656         EffectType type = lpesubtools[mode].type;
5658         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5659         bool success = lpetool_try_construction(lc, type);
5660         if (success) {
5661             // since the construction was already performed, we set the state back to inactive
5662             ege_select_one_action_set_active(act, 0);
5663             mode = 0;
5664         } else {
5665             // switch to the chosen subtool
5666             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5667         }
5669         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5670             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5671             prefs->setInt( "/tools/lpetool/mode", mode );
5672         }
5674         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5675     }
5678 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5680     SPEventContext *ec = selection->desktop()->event_context;
5681     if (!SP_IS_LPETOOL_CONTEXT(ec))
5682         return;
5684     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5687 void
5688 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5690     using namespace Inkscape::LivePathEffect;
5691     SPEventContext *ec = selection->desktop()->event_context;
5692     if (!SP_IS_LPETOOL_CONTEXT(ec))
5693         return;
5694     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5696     lpetool_delete_measuring_items(lc);
5697     lpetool_create_measuring_items(lc, selection);
5699     // activate line segment combo box if a single item with LPELineSegment is selected
5700     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5701     SPItem *item = selection->singleItem();
5702     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5703         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5704         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5705         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5706             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5707             g_object_set_data(tbl, "currentlpe", lpe);
5708             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5709             gtk_action_set_sensitive(w, TRUE);
5710             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5711         } else {
5712             g_object_set_data(tbl, "currentlpe", NULL);
5713             g_object_set_data(tbl, "currentlpeitem", NULL);
5714             gtk_action_set_sensitive(w, FALSE);
5715         }
5716     } else {
5717         g_object_set_data(tbl, "currentlpe", NULL);
5718         g_object_set_data(tbl, "currentlpeitem", NULL);
5719         gtk_action_set_sensitive(w, FALSE);
5720     }
5723 static void
5724 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5725     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5726     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5728     bool show = gtk_toggle_action_get_active( act );
5729     prefs->setBool("/tools/lpetool/show_bbox",  show);
5731     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5732         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5733         lpetool_context_reset_limiting_bbox(lc);
5734     }
5737 static void
5738 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5739     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5740     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5741         return;
5743     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5744     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5745     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5746         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5747         bool show = gtk_toggle_action_get_active( act );
5748         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5749         lpetool_show_measuring_info(lc, show);
5750         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5751     }
5754 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5755     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5756     SPUnit const *unit = tracker->getActiveUnit();
5757     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5758     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5760     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5761     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5762         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5763         lpetool_delete_measuring_items(lc);
5764         lpetool_create_measuring_items(lc);
5765     }
5768 static void
5769 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5770     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5771     Inkscape::Selection *selection = desktop->selection;
5773     Geom::OptRect bbox = selection->bounds();
5775     if (bbox) {
5776         Geom::Point A(bbox->min());
5777         Geom::Point B(bbox->max());
5779         A *= desktop->doc2dt();
5780         B *= desktop->doc2dt();
5782         // TODO: should we provide a way to store points in prefs?
5783         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5784         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5785         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5786         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5787         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5789         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5790     }
5792     gtk_toggle_action_set_active(act, false);
5795 static void
5796 sp_line_segment_build_list(GObject *tbl)
5798     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5800     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5801     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5802     gtk_list_store_clear (model);
5804     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5805     {
5806         GtkTreeIter iter;
5807         gtk_list_store_append( model, &iter );
5808         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5809         gtk_list_store_append( model, &iter );
5810         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5811         gtk_list_store_append( model, &iter );
5812         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5813         gtk_list_store_append( model, &iter );
5814         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5815     }
5817     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5820 static void
5821 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5822     using namespace Inkscape::LivePathEffect;
5824     // quit if run by the attr_changed listener
5825     if (g_object_get_data(tbl, "freeze")) {
5826         return;
5827     }
5829     // in turn, prevent listener from responding
5830     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5832     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5833     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5834     if (lpeitem) {
5835         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5836         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5837         sp_lpe_item_update_patheffect(lpeitem, true, true);
5838     }
5840     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5843 static void
5844 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5845     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5847     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5848         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5849     }
5850     gtk_toggle_action_set_active(act, false);
5853 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5855     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5856     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5857     g_object_set_data(holder, "tracker", tracker);
5858     SPUnit const *unit = tracker->getActiveUnit();
5860     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5861     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5863     /** Automatically create a list of LPEs that get added to the toolbar **/
5864     {
5865         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5867         GtkTreeIter iter;
5869         // the first toggle button represents the state that no subtool is active (remove this when
5870         // this can be modeled by EgeSelectOneAction or some other action)
5871         gtk_list_store_append( model, &iter );
5872         gtk_list_store_set( model, &iter,
5873                             0, _("All inactive"),
5874                             1, _("No geometric tool is active"),
5875                             2, "draw-geometry-inactive",
5876                             -1 );
5878         Inkscape::LivePathEffect::EffectType type;
5879         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5880             type =  lpesubtools[i].type;
5881             gtk_list_store_append( model, &iter );
5882             gtk_list_store_set( model, &iter,
5883                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5884                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5885                                 2, lpesubtools[i].icon_name,
5886                                 -1 );
5887         }
5889         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5890         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5891         g_object_set_data( holder, "lpetool_mode_action", act );
5893         ege_select_one_action_set_appearance( act, "full" );
5894         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5895         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5896         ege_select_one_action_set_icon_column( act, 2 );
5897         ege_select_one_action_set_tooltip_column( act, 1  );
5899         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5900         ege_select_one_action_set_active( act, lpeToolMode );
5901         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5902     }
5904     /* Show limiting bounding box */
5905     {
5906         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5907                                                       _("Show limiting bounding box"),
5908                                                       _("Show bounding box (used to cut infinite lines)"),
5909                                                       "show-bounding-box",
5910                                                       Inkscape::ICON_SIZE_DECORATION );
5911         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5912         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5913         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5914     }
5916     /* Set limiting bounding box to bbox of current selection */
5917     {
5918         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5919                                                       _("Get limiting bounding box from selection"),
5920                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5921                                                       "draw-geometry-set-bounding-box",
5922                                                       Inkscape::ICON_SIZE_DECORATION );
5923         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5924         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5925         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5926     }
5929     /* Combo box to choose line segment type */
5930     {
5931         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5932         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5933         ege_select_one_action_set_appearance (act, "compact");
5934         g_object_set_data (holder, "lpetool_line_segment_action", act );
5936         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5938         sp_line_segment_build_list (holder);
5940         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5941         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5942         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5943     }
5945     /* Display measuring info for selected items */
5946     {
5947         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5948                                                       _("Display measuring info"),
5949                                                       _("Display measuring info for selected items"),
5950                                                       "draw-geometry-show-measuring-info",
5951                                                       Inkscape::ICON_SIZE_DECORATION );
5952         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5953         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5954         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5955     }
5957     // add the units menu
5958     {
5959         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5960         gtk_action_group_add_action( mainActions, act );
5961         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5962         g_object_set_data(holder, "lpetool_units_action", act);
5963         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5964     }
5966     /* Open LPE dialog (to adapt parameters numerically) */
5967     {
5968         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5969                                                       _("Open LPE dialog"),
5970                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5971                                                       "dialog-geometry",
5972                                                       Inkscape::ICON_SIZE_DECORATION );
5973         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5974         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5975         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5976     }
5978     //watch selection
5979     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5981     sigc::connection *c_selection_modified =
5982         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5983                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5984     pool->add_connection ("selection-modified", c_selection_modified);
5986     sigc::connection *c_selection_changed =
5987         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5988                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5989     pool->add_connection ("selection-changed", c_selection_changed);
5992 //########################
5993 //##       Eraser       ##
5994 //########################
5996 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5998     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5999     prefs->setDouble( "/tools/eraser/width", adj->value );
6000     update_presets_list(tbl);
6003 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
6005     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6006     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
6007     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
6008         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6009         prefs->setBool( "/tools/eraser/mode", eraserMode );
6010     }
6012     // only take action if run by the attr_changed listener
6013     if (!g_object_get_data( tbl, "freeze" )) {
6014         // in turn, prevent listener from responding
6015         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6017         if ( eraserMode != 0 ) {
6018         } else {
6019         }
6020         // TODO finish implementation
6022         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6023     }
6026 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
6028     {
6029         /* Width */
6030         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
6031         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
6032         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
6033                                                               _("Pen Width"), _("Width:"),
6034                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
6035                                                               "/tools/eraser/width", 15,
6036                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
6037                                                               1, 100, 1.0, 10.0,
6038                                                               labels, values, G_N_ELEMENTS(labels),
6039                                                               sp_erc_width_value_changed, 1, 0);
6040         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
6041         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6042         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
6043     }
6045     {
6046         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
6048         GtkTreeIter iter;
6049         gtk_list_store_append( model, &iter );
6050         gtk_list_store_set( model, &iter,
6051                             0, _("Delete"),
6052                             1, _("Delete objects touched by the eraser"),
6053                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
6054                             -1 );
6056         gtk_list_store_append( model, &iter );
6057         gtk_list_store_set( model, &iter,
6058                             0, _("Cut"),
6059                             1, _("Cut out from objects"),
6060                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
6061                             -1 );
6063         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
6064         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
6065         g_object_set_data( holder, "eraser_mode_action", act );
6067         ege_select_one_action_set_appearance( act, "full" );
6068         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
6069         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
6070         ege_select_one_action_set_icon_column( act, 2 );
6071         ege_select_one_action_set_tooltip_column( act, 1  );
6073         /// @todo Convert to boolean?
6074         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6075         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
6076         ege_select_one_action_set_active( act, eraserMode );
6077         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
6078     }
6082 //########################
6083 //##    Text Toolbox    ##
6084 //########################
6085 /*
6086 static void
6087 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
6089     //Call back for letter sizing spinbutton
6092 static void
6093 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
6095     //Call back for line height spinbutton
6098 static void
6099 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6101     //Call back for horizontal kerning spinbutton
6104 static void
6105 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6107     //Call back for vertical kerning spinbutton
6110 static void
6111 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6113     //Call back for letter rotation spinbutton
6114 }*/
6116 namespace {
6118 void
6119 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6121     // quit if run by the _changed callbacks
6122     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6123         return;
6124     }
6126     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6128     SPStyle *query =
6129         sp_style_new (SP_ACTIVE_DOCUMENT);
6131     int result_family =
6132         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6134     int result_style =
6135         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6137     int result_numbers =
6138         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6140     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6142     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6143     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6144         // there are no texts in selection, read from prefs
6146         sp_style_read_from_prefs(query, "/tools/text");
6148         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6149             // do not reset the toolbar style from prefs if we already did it last time
6150             sp_style_unref(query);
6151             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6152             return;
6153         }
6154         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6155     } else {
6156         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6157     }
6159     if (query->text)
6160     {
6161         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6162             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6163             gtk_entry_set_text (GTK_ENTRY (entry), "");
6165         } else if (query->text->font_specification.value || query->text->font_family.value) {
6167             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6168             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6170             // Get the font that corresponds
6171             Glib::ustring familyName;
6173             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6174             if (font) {
6175                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6176                 font->Unref();
6177                 font = NULL;
6178             }
6180             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6182             Gtk::TreeIter iter;
6183             try {
6184                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6185                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6186                 iter = model->get_iter(path);
6187             } catch (...) {
6188                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6189                 sp_style_unref(query);
6190                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6191                 return;
6192             }
6194             combo->set_active (iter);
6195         }
6197         //Size
6198         {
6199             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6200             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6201             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6202             g_free(str);
6203         }
6205         //Anchor
6206         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6207         {
6208             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6209             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6210             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6211             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6212         }
6213         else
6214         {
6215             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6216             {
6217                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6218                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6219                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6220                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6221             }
6222             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6223             {
6224                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6225                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6226                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6227                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6228             }
6229             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6230             {
6231                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6232                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6233                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6234                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6235             }
6236         }
6238         //Style
6239         {
6240             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6242             gboolean active = gtk_toggle_button_get_active (button);
6243             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));
6245             if (active != check)
6246             {
6247                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6248                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6249                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6250             }
6251         }
6253         {
6254             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6256             gboolean active = gtk_toggle_button_get_active (button);
6257             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6259             if (active != check)
6260             {
6261                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6262                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6263                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6264             }
6265         }
6267         //Orientation
6268         //locking both buttons, changing one affect all group (both)
6269         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6270         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6272         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6273         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6275         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6276         {
6277             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6278         }
6279         else
6280         {
6281             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6282         }
6283         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6284         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6285     }
6287     sp_style_unref(query);
6289     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6292 void
6293 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6295     sp_text_toolbox_selection_changed (selection, tbl);
6298 void
6299 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6301     sp_text_toolbox_selection_changed (NULL, tbl);
6304 void
6305 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6306                                 GObject             *tbl)
6308     // quit if run by the _changed callbacks
6309     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6310         return;
6311     }
6313     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6315     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6316     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6317     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6319     //g_print ("family changed to: %s\n", family);
6321     SPStyle *query =
6322         sp_style_new (SP_ACTIVE_DOCUMENT);
6324     int result_fontspec =
6325         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6327     SPCSSAttr *css = sp_repr_css_attr_new ();
6329     // First try to get the font spec from the stored value
6330     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6332     if (fontSpec.empty()) {
6333         // Construct a new font specification if it does not yet exist
6334         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6335         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6336         fontFromStyle->Unref();
6337     }
6339     if (!fontSpec.empty()) {
6341         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6343         if (!newFontSpec.empty()) {
6345             if (fontSpec != newFontSpec) {
6347                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6349                 if (font) {
6350                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6352                     // Set all the these just in case they were altered when finding the best
6353                     // match for the new family and old style...
6355                     gchar c[256];
6357                     font->Family(c, 256);
6359                     sp_repr_css_set_property (css, "font-family", c);
6361                     font->Attribute( "weight", c, 256);
6362                     sp_repr_css_set_property (css, "font-weight", c);
6364                     font->Attribute("style", c, 256);
6365                     sp_repr_css_set_property (css, "font-style", c);
6367                     font->Attribute("stretch", c, 256);
6368                     sp_repr_css_set_property (css, "font-stretch", c);
6370                     font->Attribute("variant", c, 256);
6371                     sp_repr_css_set_property (css, "font-variant", c);
6373                     font->Unref();
6374                 }
6375             }
6377         } else {
6378             // If the old font on selection (or default) was not existing on the system,
6379             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6380             // setting the family reported by the family chooser.
6382             //g_print ("fallback setting family: %s\n", family);
6383             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6384             sp_repr_css_set_property (css, "font-family", family);
6385         }
6386     }
6388     // If querying returned nothing, set the default style of the tool (for new texts)
6389     if (result_fontspec == QUERY_STYLE_NOTHING)
6390     {
6391         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6392         prefs->mergeStyle("/tools/text/style", css);
6393         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6394     }
6395     else
6396     {
6397         sp_desktop_set_style (desktop, css, true, true);
6398     }
6400     sp_style_unref(query);
6402     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6403                                    _("Text: Change font family"));
6404     sp_repr_css_attr_unref (css);
6406     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6408     // unfreeze
6409     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6411     // focus to canvas
6412     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6416 void
6417 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6418                                    gpointer          data)
6420     if (g_object_get_data (G_OBJECT (button), "block")) return;
6421     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6422     int prop = GPOINTER_TO_INT(data);
6424     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6426     // move the x of all texts to preserve the same bbox
6427     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6428     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6429         if (SP_IS_TEXT((SPItem *) items->data)) {
6430             SPItem *item = SP_ITEM(items->data);
6432             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6433             // below, variable names suggest horizontal move, but we check the writing direction
6434             // and move in the corresponding axis
6435             int axis;
6436             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6437                 axis = NR::X;
6438             } else {
6439                 axis = NR::Y;
6440             }
6442             Geom::OptRect bbox
6443                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6444             if (!bbox)
6445                 continue;
6446             double width = bbox->dimensions()[axis];
6447             // If you want to align within some frame, other than the text's own bbox, calculate
6448             // the left and right (or top and bottom for tb text) slacks of the text inside that
6449             // frame (currently unused)
6450             double left_slack = 0;
6451             double right_slack = 0;
6452             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6453             double move = 0;
6454             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6455                 switch (prop) {
6456                     case 0:
6457                         move = -left_slack;
6458                         break;
6459                     case 1:
6460                         move = width/2 + (right_slack - left_slack)/2;
6461                         break;
6462                     case 2:
6463                         move = width + right_slack;
6464                         break;
6465                 }
6466             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6467                 switch (prop) {
6468                     case 0:
6469                         move = -width/2 - left_slack;
6470                         break;
6471                     case 1:
6472                         move = (right_slack - left_slack)/2;
6473                         break;
6474                     case 2:
6475                         move = width/2 + right_slack;
6476                         break;
6477                 }
6478             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6479                 switch (prop) {
6480                     case 0:
6481                         move = -width - left_slack;
6482                         break;
6483                     case 1:
6484                         move = -width/2 + (right_slack - left_slack)/2;
6485                         break;
6486                     case 2:
6487                         move = right_slack;
6488                         break;
6489                 }
6490             }
6491             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6492             if (axis == NR::X) {
6493                 XY = XY + Geom::Point (move, 0);
6494             } else {
6495                 XY = XY + Geom::Point (0, move);
6496             }
6497             SP_TEXT(item)->attributes.setFirstXY(XY);
6498             SP_OBJECT(item)->updateRepr();
6499             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6500         }
6501     }
6503     SPCSSAttr *css = sp_repr_css_attr_new ();
6504     switch (prop)
6505     {
6506         case 0:
6507         {
6508             sp_repr_css_set_property (css, "text-anchor", "start");
6509             sp_repr_css_set_property (css, "text-align", "start");
6510             break;
6511         }
6512         case 1:
6513         {
6514             sp_repr_css_set_property (css, "text-anchor", "middle");
6515             sp_repr_css_set_property (css, "text-align", "center");
6516             break;
6517         }
6519         case 2:
6520         {
6521             sp_repr_css_set_property (css, "text-anchor", "end");
6522             sp_repr_css_set_property (css, "text-align", "end");
6523             break;
6524         }
6526         case 3:
6527         {
6528             sp_repr_css_set_property (css, "text-anchor", "start");
6529             sp_repr_css_set_property (css, "text-align", "justify");
6530             break;
6531         }
6532     }
6534     SPStyle *query =
6535         sp_style_new (SP_ACTIVE_DOCUMENT);
6536     int result_numbers =
6537         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6539     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6540     if (result_numbers == QUERY_STYLE_NOTHING)
6541     {
6542         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6543         prefs->mergeStyle("/tools/text/style", css);
6544     }
6546     sp_style_unref(query);
6548     sp_desktop_set_style (desktop, css, true, true);
6549     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6550                                    _("Text: Change alignment"));
6551     sp_repr_css_attr_unref (css);
6553     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6556 void
6557 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6558                                gpointer          data)
6560     if (g_object_get_data (G_OBJECT (button), "block")) return;
6562     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6563     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6564     int          prop       = GPOINTER_TO_INT(data);
6565     bool         active     = gtk_toggle_button_get_active (button);
6567     SPStyle *query =
6568         sp_style_new (SP_ACTIVE_DOCUMENT);
6570     int result_fontspec =
6571         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6573     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6574     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6575     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6577     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6578     Glib::ustring newFontSpec = "";
6580     if (fontSpec.empty()) {
6581         // Construct a new font specification if it does not yet exist
6582         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6583         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6584         fontFromStyle->Unref();
6585     }
6587     bool nochange = true;
6588     switch (prop)
6589     {
6590         case 0:
6591         {
6592             if (!fontSpec.empty()) {
6593                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6594                 if (!newFontSpec.empty()) {
6595                     // Don't even set the bold if the font didn't exist on the system
6596                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6597                     nochange = false;
6598                 }
6599             }
6600             // set or reset the button according
6601             if(nochange) {
6602                 gboolean check = gtk_toggle_button_get_active (button);
6604                 if (active != check)
6605                 {
6606                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6607                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6608                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6609                 }
6610             }
6612             break;
6613         }
6615         case 1:
6616         {
6617             if (!fontSpec.empty()) {
6618                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6619                 if (!newFontSpec.empty()) {
6620                     // Don't even set the italic if the font didn't exist on the system
6621                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6622                     nochange = false;
6623                 }
6624             }
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             }
6635             break;
6636         }
6637     }
6639     if (!newFontSpec.empty()) {
6640         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6641     }
6643     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6644     if (result_fontspec == QUERY_STYLE_NOTHING)
6645     {
6646         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6647         prefs->mergeStyle("/tools/text/style", css);
6648     }
6650     sp_style_unref(query);
6652     sp_desktop_set_style (desktop, css, true, true);
6653     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6654                                    _("Text: Change font style"));
6655     sp_repr_css_attr_unref (css);
6657     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6660 void
6661 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6662                                      gpointer         data)
6664     if (g_object_get_data (G_OBJECT (button), "block")) {
6665         return;
6666     }
6668     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6669     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6670     int          prop       = GPOINTER_TO_INT(data);
6672     switch (prop)
6673     {
6674         case 0:
6675         {
6676             sp_repr_css_set_property (css, "writing-mode", "lr");
6677             break;
6678         }
6680         case 1:
6681         {
6682             sp_repr_css_set_property (css, "writing-mode", "tb");
6683             break;
6684         }
6685     }
6687     SPStyle *query =
6688         sp_style_new (SP_ACTIVE_DOCUMENT);
6689     int result_numbers =
6690         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6692     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6693     if (result_numbers == QUERY_STYLE_NOTHING)
6694     {
6695         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6696         prefs->mergeStyle("/tools/text/style", css);
6697     }
6699     sp_desktop_set_style (desktop, css, true, true);
6700     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6701                                    _("Text: Change orientation"));
6702     sp_repr_css_attr_unref (css);
6704     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6707 gboolean
6708 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6710     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6711     if (!desktop) return FALSE;
6713     switch (get_group0_keyval (event)) {
6714         case GDK_KP_Enter: // chosen
6715         case GDK_Return:
6716             // unfreeze and update, which will defocus
6717             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6718             sp_text_toolbox_family_changed (NULL, tbl); 
6719             return TRUE; // I consumed the event
6720             break;
6721         case GDK_Escape: 
6722             // defocus
6723             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6724             return TRUE; // I consumed the event
6725             break;
6726     }
6727     return FALSE;
6730 gboolean
6731 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6733     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6734     if (!desktop) return FALSE;
6736     switch (get_group0_keyval (event)) {
6737         case GDK_KP_Enter:
6738         case GDK_Return:
6739         case GDK_Escape: // defocus
6740             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6741             return TRUE; // I consumed the event
6742             break;
6743         case GDK_w:
6744         case GDK_W:
6745             if (event->state & GDK_CONTROL_MASK) {
6746                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6747                 return TRUE; // I consumed the event
6748             }
6749             break;
6750     }
6751     return FALSE;
6755 void
6756 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6757                                GObject     *tbl)
6759      // quit if run by the _changed callbacks
6760     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6761         return;
6762     }
6764     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6766    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6768     // If this is not from selecting a size in the list (in which case get_active will give the
6769     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6770     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6771     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6772    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6773         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6774         return;
6775    }
6777     gdouble value = -1;
6778     {
6779         gchar *endptr;
6780         gchar *const text = gtk_combo_box_get_active_text(cbox);
6781         if (text) {
6782             value = g_strtod(text, &endptr);
6783             if (endptr == text) {  // Conversion failed, non-numeric input.
6784                 value = -1;
6785             }
6786             g_free(text);
6787         }
6788     }
6789     if (value <= 0) {
6790         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6791         return; // could not parse value
6792     }
6794     SPCSSAttr *css = sp_repr_css_attr_new ();
6795     Inkscape::CSSOStringStream osfs;
6796     osfs << value;
6797     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6799     SPStyle *query =
6800         sp_style_new (SP_ACTIVE_DOCUMENT);
6801     int result_numbers =
6802         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6804     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6805     if (result_numbers == QUERY_STYLE_NOTHING)
6806     {
6807         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6808         prefs->mergeStyle("/tools/text/style", css);
6809     }
6811     sp_style_unref(query);
6813     sp_desktop_set_style (desktop, css, true, true);
6814     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6815                                    _("Text: Change font size"));
6816     sp_repr_css_attr_unref (css);
6818     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6820     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6823 gboolean
6824 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6826     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6827     if (!desktop) return FALSE;
6829     if (!g_object_get_data (tbl, "esc-pressed")) {
6830         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6831         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6832         sp_text_toolbox_size_changed (cbox, tbl);
6833         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6834     }
6835     return FALSE; // I consumed the event
6839 gboolean
6840 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6842     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6843     if (!desktop) return FALSE;
6845     switch (get_group0_keyval (event)) {
6846         case GDK_Escape: // defocus
6847             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6848             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6849             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6850             return TRUE; // I consumed the event
6851             break;
6852         case GDK_Return: // defocus
6853         case GDK_KP_Enter:
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             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6858             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6859             return TRUE; // I consumed the event
6860             break;
6861     }
6862     return FALSE;
6865 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6866 // does not work!
6867 gboolean
6868 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6869                                  GdkEventFocus    */*event*/,
6870                                  GObject          *tbl)
6872     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6873     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6874     return FALSE;
6877 gboolean
6878 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6879                                  GdkEventFocus    */*event*/,
6880                                  GObject          *tbl)
6882     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6883     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6884     return FALSE;
6887 void
6888 cell_data_func  (GtkCellLayout */*cell_layout*/,
6889                  GtkCellRenderer   *cell,
6890                  GtkTreeModel      *tree_model,
6891                  GtkTreeIter       *iter,
6892                  gpointer           /*data*/)
6894     gchar *family;
6895     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6896     gchar *const family_escaped = g_markup_escape_text(family, -1);
6898     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6899     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6900     if (show_sample) {
6902         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6903         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6905     std::stringstream markup;
6906     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6907            << family_escaped << "'>" << sample_escaped << "</span>";
6908     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6910         g_free(sample_escaped);
6911     } else {
6912         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6913     }
6915     g_free(family);
6916     g_free(family_escaped);
6919 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6920                                                 GtkTreeModel       *model,
6921                                                 GtkTreeIter        *iter,
6922                                                 GObject            *tbl)
6924     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6925     // press Enter again after choosing a completion)
6926     gchar *family = 0;
6927     gtk_tree_model_get(model, iter, 0, &family, -1);
6929     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6930     gtk_entry_set_text (GTK_ENTRY (entry), family);
6932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6933     sp_text_toolbox_family_changed (NULL, tbl);
6934     return TRUE;
6938 static void
6939 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6940     GtkEntry *entry;
6941     GtkEntryCompletion *completion;
6942     GtkTreeModel *model;
6944     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6945     completion = gtk_entry_completion_new();
6946     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6947     gtk_entry_completion_set_model(completion, model);
6948     gtk_entry_completion_set_text_column(completion, 0);
6949     gtk_entry_completion_set_inline_completion(completion, FALSE);
6950     gtk_entry_completion_set_inline_selection(completion, FALSE);
6951     gtk_entry_completion_set_popup_completion(completion, TRUE);
6952     gtk_entry_set_completion(entry, completion);
6954     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6956     g_object_unref(completion);
6959 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6960                                       void */*property*/,
6961                                       GObject *tbl)
6963   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6965   gboolean shown;
6966   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6967   if (shown) {
6968          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6969          //g_print("POP: notify: SHOWN\n");
6970   } else {
6971          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6973          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6974          // find out if the drop down was closed by Enter and if so, manually update (only
6975          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6976          GdkEvent *ev = gtk_get_current_event();
6977          if (ev) {
6978              //g_print ("ev type: %d\n", ev->type);
6979              if (ev->type == GDK_KEY_PRESS) {
6980                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6981                      case GDK_KP_Enter: // chosen
6982                      case GDK_Return:
6983                      {
6984                          // make sure the chosen one is inserted into the entry
6985                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6986                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6987                          GtkTreeIter iter;
6988                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6989                          if (has_active) {
6990                              gchar *family;
6991                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6992                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6993                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6994                          }
6996                          // update
6997                          sp_text_toolbox_family_changed (NULL, tbl); 
6998                          break;
6999                      }
7000                  } 
7001              }
7002          }
7004          // regardless of whether we updated, defocus the widget
7005          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
7006          if (desktop)
7007              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
7008          //g_print("POP: notify: HIDDEN\n");
7009   }
7012 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
7014     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
7015     GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
7017     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
7018     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
7020     GtkTooltips *tt = gtk_tooltips_new();
7022     ////////////Family
7023     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
7024     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
7026     gtk_rc_parse_string (
7027        "style \"dropdown-as-list-style\"\n"
7028        "{\n"
7029        "    GtkComboBox::appears-as-list = 1\n"
7030        "}\n"
7031        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
7032     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
7033     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
7035     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
7037     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
7038     
7039     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
7040     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
7042     // expand the field a bit so as to view more of the previews in the drop-down
7043     GtkRequisition req;
7044     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
7045     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
7047     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
7048     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7050     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
7051     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown", 
7052              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
7053     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
7054     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
7055     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
7057     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
7058     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
7060     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
7061     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
7062     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
7063     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
7065     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
7066     GtkWidget *box = gtk_event_box_new ();
7067     gtk_container_add (GTK_CONTAINER (box), image);
7068     gtk_toolbar_append_widget( tbl, box, "", "");
7069     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
7070     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
7071     gtk_widget_hide (GTK_WIDGET (box));
7072     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
7074     ////////////Size
7075     gchar const *const sizes[] = {
7076         "4", "6", "8", "9", "10", "11", "12", "13", "14",
7077         "16", "18", "20", "22", "24", "28",
7078         "32", "36", "40", "48", "56", "64", "72", "144"
7079     };
7081     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
7082     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
7083         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
7084     }
7085     gtk_widget_set_size_request (cbox, 80, -1);
7086     gtk_toolbar_append_widget( tbl, cbox, "", "");
7087     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
7088     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
7089     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
7090     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
7092     ////////////Text anchor
7093     GtkWidget *group   = gtk_radio_button_new (NULL);
7094     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
7095     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
7097     // left
7098     GtkWidget *rbutton = group;
7099     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7100     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
7101     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7103     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7104     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7105     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7106     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7108     // center
7109     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7110     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7111     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7112     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7114     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7115     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7116     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7117     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7119     // right
7120     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7121     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7122     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7123     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7125     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7126     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7127     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7128     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7130     // fill
7131     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7132     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7133     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7134     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7136     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7137     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7138     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7139     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7141     gtk_toolbar_append_widget( tbl, row, "", "");
7143     //spacer
7144     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7146     ////////////Text style
7147     row = gtk_hbox_new (FALSE, 4);
7149     // bold
7150     rbutton = gtk_toggle_button_new ();
7151     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7152     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7153     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7154     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7156     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7157     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7158     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7160     // italic
7161     rbutton = gtk_toggle_button_new ();
7162     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7163     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7164     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7165     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7167     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7168     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7169     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7171     gtk_toolbar_append_widget( tbl, row, "", "");
7173     //spacer
7174     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7176     // Text orientation
7177     group   = gtk_radio_button_new (NULL);
7178     row     = gtk_hbox_new (FALSE, 4);
7179     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7181     // horizontal
7182     rbutton = group;
7183     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7184     gtk_container_add           (GTK_CONTAINER (rbutton),
7185                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7186     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7187     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7189     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7190     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7191     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7193     // vertical
7194     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7195     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7196     gtk_container_add           (GTK_CONTAINER (rbutton),
7197                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7198     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7199     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7201     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7202     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7203     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7204     gtk_toolbar_append_widget( tbl, row, "", "" );
7207     //watch selection
7208     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7210     sigc::connection *c_selection_changed =
7211         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7212                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7213     pool->add_connection ("selection-changed", c_selection_changed);
7215     sigc::connection *c_selection_modified =
7216         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7217                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7218     pool->add_connection ("selection-modified", c_selection_modified);
7220     sigc::connection *c_subselection_changed =
7221         new sigc::connection (desktop->connectToolSubselectionChanged
7222                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7223     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7225     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7228     gtk_widget_show_all( GTK_WIDGET(tbl) );
7230     return GTK_WIDGET(tbl);
7231 } // end of sp_text_toolbox_new()
7233 }//<unnamed> namespace
7236 //#########################
7237 //##      Connector      ##
7238 //#########################
7240 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7242     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7243     prefs->setBool("/tools/connector/mode",
7244                 gtk_toggle_action_get_active( act ));
7247 static void sp_connector_path_set_avoid(void)
7249     cc_selection_set_avoid(true);
7253 static void sp_connector_path_set_ignore(void)
7255     cc_selection_set_avoid(false);
7258 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7260     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7261     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7262     SPDocument *doc = sp_desktop_document(desktop);
7264     if (!sp_document_get_undo_sensitive(doc))
7265     {
7266         return;
7267     }
7270     // quit if run by the _changed callbacks
7271     if (g_object_get_data( tbl, "freeze" )) {
7272         return;
7273     }
7275     // in turn, prevent callbacks from responding
7276     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7278     bool is_orthog = gtk_toggle_action_get_active( act );
7279     gchar orthog_str[] = "orthogonal";
7280     gchar polyline_str[] = "polyline";
7281     gchar *value = is_orthog ? orthog_str : polyline_str ;
7283     bool modmade = false;
7284     GSList *l = (GSList *) selection->itemList();
7285     while (l) {
7286         SPItem *item = (SPItem *) l->data;
7288         if (cc_item_is_connector(item)) {
7289             sp_object_setAttribute(item, "inkscape:connector-type",
7290                     value, false);
7291             item->avoidRef->handleSettingChange();
7292             modmade = true;
7293         }
7294         l = l->next;
7295     }
7297     if (!modmade)
7298     {
7299         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7300         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7301     }
7303     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7304             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7306     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7309 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7311     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7312     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7313     SPDocument *doc = sp_desktop_document(desktop);
7315     if (!sp_document_get_undo_sensitive(doc))
7316     {
7317         return;
7318     }
7321     // quit if run by the _changed callbacks
7322     if (g_object_get_data( tbl, "freeze" )) {
7323         return;
7324     }
7326     // in turn, prevent callbacks from responding
7327     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7329     gdouble newValue = gtk_adjustment_get_value(adj);
7330     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7331     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7333     bool modmade = false;
7334     GSList *l = (GSList *) selection->itemList();
7335     while (l) {
7336         SPItem *item = (SPItem *) l->data;
7338         if (cc_item_is_connector(item)) {
7339             sp_object_setAttribute(item, "inkscape:connector-curvature",
7340                     value, false);
7341             item->avoidRef->handleSettingChange();
7342             modmade = true;
7343         }
7344         l = l->next;
7345     }
7347     if (!modmade)
7348     {
7349         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7350         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7351     }
7353     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7354             _("Change connector curvature"));
7356     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7360 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7362     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7363     SPDocument *doc = sp_desktop_document(desktop);
7365     if (!sp_document_get_undo_sensitive(doc))
7366     {
7367         return;
7368     }
7370     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7372     if ( !repr->attribute("inkscape:connector-spacing") &&
7373             ( adj->value == defaultConnSpacing )) {
7374         // Don't need to update the repr if the attribute doesn't 
7375         // exist and it is being set to the default value -- as will
7376         // happen at startup.
7377         return;
7378     }
7380     // quit if run by the attr_changed listener
7381     if (g_object_get_data( tbl, "freeze" )) {
7382         return;
7383     }
7385     // in turn, prevent listener from responding
7386     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7388     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7389     SP_OBJECT(desktop->namedview)->updateRepr();
7391     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7392     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7393         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7394         Geom::Matrix m = Geom::identity();
7395         avoid_item_move(&m, item);
7396     }
7398     if (items) {
7399         g_slist_free(items);
7400     }
7402     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7403             _("Change connector spacing"));
7405     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7408 static void sp_connector_graph_layout(void)
7410     if (!SP_ACTIVE_DESKTOP) return;
7411     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7413     // hack for clones, see comment in align-and-distribute.cpp
7414     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7415     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7417     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7419     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7421     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7424 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7426     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7427     prefs->setBool("/tools/connector/directedlayout",
7428                 gtk_toggle_action_get_active( act ));
7431 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7433     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7434     prefs->setBool("/tools/connector/avoidoverlaplayout",
7435                 gtk_toggle_action_get_active( act ));
7439 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7441     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7442     prefs->setDouble("/tools/connector/length", adj->value);
7445 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7446                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7447                                             bool /*is_interactive*/, gpointer data)
7449     GtkWidget *tbl = GTK_WIDGET(data);
7451     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7452         return;
7453     }
7454     if (strcmp(name, "inkscape:connector-spacing") == 0)
7455     {
7456         GtkAdjustment *adj = (GtkAdjustment*)
7457                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7458         gdouble spacing = defaultConnSpacing;
7459         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7461         gtk_adjustment_set_value(adj, spacing);
7462         gtk_adjustment_value_changed(adj);
7463     }
7465     spinbutton_defocus(GTK_OBJECT(tbl));
7468 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7470     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7471     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7473     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7474         cc_create_connection_point(cc);
7477 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7479     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7480     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7482     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7483         cc_remove_connection_point(cc);
7486 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7487     NULL, /* child_added */
7488     NULL, /* child_removed */
7489     connector_tb_event_attr_changed,
7490     NULL, /* content_changed */
7491     NULL  /* order_changed */
7492 };
7494 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7496     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7497     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7498     SPItem *item = selection->singleItem();
7499     if (SP_IS_PATH(item))
7500     {
7501         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7502         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7503         gtk_toggle_action_set_active(act, is_orthog);
7504         gtk_adjustment_set_value(adj, curvature);
7505     }
7509 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7511     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7512     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7514     // Editing mode toggle button
7515     {
7516         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7517                                                       _("EditMode"),
7518                                                       _("Switch between connection point editing and connector drawing mode"),
7519                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7520                                                       Inkscape::ICON_SIZE_DECORATION );
7521         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7523         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7524         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7525         g_object_set_data( holder, "mode", act );
7526         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7527     }
7530     {
7531         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7532                                           _("Avoid"),
7533                                           _("Make connectors avoid selected objects"),
7534                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7535                                           secondarySize );
7536         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7537         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7538     }
7540     {
7541         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7542                                           _("Ignore"),
7543                                           _("Make connectors ignore selected objects"),
7544                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7545                                           secondarySize );
7546         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7547         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7548     }
7550     // Orthogonal connectors toggle button
7551     {
7552         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7553                                                       _("Orthogonal"),
7554                                                       _("Make connector orthogonal or polyline"),
7555                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7556                                                       Inkscape::ICON_SIZE_DECORATION );
7557         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7559         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7560         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7561         g_object_set_data( holder, "orthogonal", act );
7562         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7563     }
7565     EgeAdjustmentAction* eact = 0;
7566     // Curvature spinbox
7567     eact = create_adjustment_action( "ConnectorCurvatureAction",
7568                                     _("Connector Curvature"), _("Curvature:"),
7569                                     _("The amount of connectors curvature"),
7570                                     "/tools/connector/curvature", defaultConnCurvature,
7571                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7572                                     0, 100, 1.0, 10.0,
7573                                     0, 0, 0,
7574                                     connector_curvature_changed, 1, 0 );
7575     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7577     // Spacing spinbox
7578     eact = create_adjustment_action( "ConnectorSpacingAction",
7579                                     _("Connector Spacing"), _("Spacing:"),
7580                                     _("The amount of space left around objects by auto-routing connectors"),
7581                                     "/tools/connector/spacing", defaultConnSpacing,
7582                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7583                                     0, 100, 1.0, 10.0,
7584                                     0, 0, 0,
7585                                     connector_spacing_changed, 1, 0 );
7586     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7588     // Graph (connector network) layout
7589     {
7590         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7591                                           _("Graph"),
7592                                           _("Nicely arrange selected connector network"),
7593                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7594                                           secondarySize );
7595         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7596         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7597     }
7599     // Default connector length spinbox
7600     eact = create_adjustment_action( "ConnectorLengthAction",
7601                                      _("Connector Length"), _("Length:"),
7602                                      _("Ideal length for connectors when layout is applied"),
7603                                      "/tools/connector/length", 100,
7604                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7605                                      10, 1000, 10.0, 100.0,
7606                                      0, 0, 0,
7607                                      connector_length_changed, 1, 0 );
7608     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7611     // Directed edges toggle button
7612     {
7613         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7614                                                       _("Downwards"),
7615                                                       _("Make connectors with end-markers (arrows) point downwards"),
7616                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7617                                                       Inkscape::ICON_SIZE_DECORATION );
7618         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7620         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7621         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7623         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7624         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7625         );
7626     }
7628     // Avoid overlaps toggle button
7629     {
7630         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7631                                                       _("Remove overlaps"),
7632                                                       _("Do not allow overlapping shapes"),
7633                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7634                                                       Inkscape::ICON_SIZE_DECORATION );
7635         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7637         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7638         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7640         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7641     }
7644     // New connection point button
7645     {
7646         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7647                                           _("New connection point"),
7648                                           _("Add a new connection point to the currently selected item"),
7649                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7650                                           secondarySize );
7651         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7652         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7653     }
7655     // Remove selected connection point button
7657     {
7658         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7659                                           _("Remove connection point"),
7660                                           _("Remove the currently selected connection point"),
7661                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7662                                           secondarySize );
7663         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7664         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7665     }
7668     // Code to watch for changes to the connector-spacing attribute in
7669     // the XML.
7670     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7671     g_assert(repr != NULL);
7673     purge_repr_listener( holder, holder );
7675     if (repr) {
7676         g_object_set_data( holder, "repr", repr );
7677         Inkscape::GC::anchor(repr);
7678         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7679         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7680     }
7681 } // end of sp_connector_toolbox_prep()
7684 //#########################
7685 //##     Paintbucket     ##
7686 //#########################
7688 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7690     gint channels = ege_select_one_action_get_active( act );
7691     flood_channels_set_channels( channels );
7694 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7696     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7697     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7700 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7702     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7703     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7706 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7708     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7709     SPUnit const *unit = tracker->getActiveUnit();
7710     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7712     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7713     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7716 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7718     // FIXME: make defaults settable via Inkscape Options
7719     struct KeyValue {
7720         char const *key;
7721         double value;
7722     } const key_values[] = {
7723         {"threshold", 15},
7724         {"offset", 0.0}
7725     };
7727     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7728         KeyValue const &kv = key_values[i];
7729         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7730         if ( adj ) {
7731             gtk_adjustment_set_value(adj, kv.value);
7732         }
7733     }
7735     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7736     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7737     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7738     ege_select_one_action_set_active( autogap_action, 0 );
7741 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7743     EgeAdjustmentAction* eact = 0;
7744     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7746     {
7747         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7749         GList* items = 0;
7750         gint count = 0;
7751         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7752         {
7753             GtkTreeIter iter;
7754             gtk_list_store_append( model, &iter );
7755             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7756             count++;
7757         }
7758         g_list_free( items );
7759         items = 0;
7760         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7761         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7762         ege_select_one_action_set_appearance( act1, "compact" );
7763         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7764         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7765         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7766         g_object_set_data( holder, "channels_action", act1 );
7767     }
7769     // Spacing spinbox
7770     {
7771         eact = create_adjustment_action(
7772             "ThresholdAction",
7773             _("Fill Threshold"), _("Threshold:"),
7774             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7775             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7776             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7777             0, 0, 0,
7778             paintbucket_threshold_changed, 1, 0 );
7780         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7782     }
7784     // Create the units menu.
7785     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7786     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7787     if (!stored_unit.empty())
7788         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7789     g_object_set_data( holder, "tracker", tracker );
7790     {
7791         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7792         gtk_action_group_add_action( mainActions, act );
7793     }
7795     // Offset spinbox
7796     {
7797         eact = create_adjustment_action(
7798             "OffsetAction",
7799             _("Grow/shrink by"), _("Grow/shrink by:"),
7800             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7801             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7802             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7803             0, 0, 0,
7804             paintbucket_offset_changed, 1, 2);
7805         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7807         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7808     }
7810     /* Auto Gap */
7811     {
7812         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7814         GList* items = 0;
7815         gint count = 0;
7816         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7817         {
7818             GtkTreeIter iter;
7819             gtk_list_store_append( model, &iter );
7820             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7821             count++;
7822         }
7823         g_list_free( items );
7824         items = 0;
7825         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7826         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7827         ege_select_one_action_set_appearance( act2, "compact" );
7828         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7829         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7830         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7831         g_object_set_data( holder, "autogap_action", act2 );
7832     }
7834     /* Reset */
7835     {
7836         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7837                                           _("Defaults"),
7838                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7839                                           GTK_STOCK_CLEAR );
7840         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7841         gtk_action_group_add_action( mainActions, act );
7842         gtk_action_set_sensitive( act, TRUE );
7843     }
7847 /*
7848   Local Variables:
7849   mode:c++
7850   c-file-style:"stroustrup"
7851   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7852   indent-tabs-mode:nil
7853   fill-column:99
7854   End:
7855 */
7856 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :