Code

Avoid crash by uninitialized perspectives.
[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@users.sf.net>
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 "../node-context.h"
69 #include "../pen-context.h"
70 #include "../preferences.h"
71 #include "../selection-chemistry.h"
72 #include "../selection.h"
73 #include "select-toolbar.h"
74 #include "../shape-editor.h"
75 #include "../shortcuts.h"
76 #include "../sp-clippath.h"
77 #include "../sp-ellipse.h"
78 #include "../sp-flowtext.h"
79 #include "../sp-mask.h"
80 #include "../sp-namedview.h"
81 #include "../sp-rect.h"
82 #include "../sp-spiral.h"
83 #include "../sp-star.h"
84 #include "../sp-text.h"
85 #include "../style.h"
86 #include "../svg/css-ostringstream.h"
87 #include "../tools-switch.h"
88 #include "../tweak-context.h"
89 #include "../spray-context.h"
90 #include "../ui/dialog/calligraphic-profile-rename.h"
91 #include "../ui/icon-names.h"
92 #include "../ui/widget/style-swatch.h"
93 #include "../verbs.h"
94 #include "../widgets/button.h"
95 #include "../widgets/spinbutton-events.h"
96 #include "../widgets/spw-utilities.h"
97 #include "../widgets/widget-sizes.h"
98 #include "../xml/attribute-record.h"
99 #include "../xml/node-event-vector.h"
100 #include "../xml/repr.h"
102 #include "toolbox.h"
104 using Inkscape::UnitTracker;
106 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
107 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
109 enum BarId {
110     BAR_TOOL = 0,
111     BAR_AUX,
112     BAR_COMMANDS,
113     BAR_SNAP,
114 };
116 #define BAR_ID_KEY "BarIdValue"
119 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
121 static void       sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
125 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
127 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
128 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
129 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
130 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
131 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
132 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
133 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
134 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
135 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
136 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
138 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
140 using Inkscape::UI::ToolboxFactory;
143 Inkscape::IconSize ToolboxFactory::prefToSize( Glib::ustring const &path, int base ) {
144     static Inkscape::IconSize sizeChoices[] = {
145         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
146         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
147         Inkscape::ICON_SIZE_MENU
148     };
149     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
150     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
151     return sizeChoices[index];
154 static struct {
155     gchar const *type_name;
156     gchar const *data_name;
157     sp_verb_t verb;
158     sp_verb_t doubleclick_verb;
159 } const tools[] = {
160     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
161     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
162     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
163     { "SPSprayContext",    "spray_tool",     SP_VERB_CONTEXT_SPRAY, SP_VERB_CONTEXT_SPRAY_PREFS },
164     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
165     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
166     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
167     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
168     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
169     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
170     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
171     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
172     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
173     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
174     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
175     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
176     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
177     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
178     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
179     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
180     { NULL, NULL, 0, 0 }
181 };
183 static struct {
184     gchar const *type_name;
185     gchar const *data_name;
186     GtkWidget *(*create_func)(SPDesktop *desktop);
187     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
188     gchar const *ui_name;
189     gint swatch_verb_id;
190     gchar const *swatch_tool;
191     gchar const *swatch_tip;
192 } const aux_toolboxes[] = {
193     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
194       SP_VERB_INVALID, 0, 0},
195     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
196       SP_VERB_INVALID, 0, 0},
197     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
198       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
199     { "SPSprayContext",   "spray_toolbox",   0, sp_spray_toolbox_prep,              "SprayToolbar",
200       SP_VERB_CONTEXT_SPRAY_PREFS, "/tools/spray", N_("Color/opacity used for color spraying")},
201     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
202       SP_VERB_INVALID, 0, 0},
203     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
204       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
205     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
206       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
207     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
208       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
209     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
210       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
211     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
212       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
213     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
214       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
215     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
216       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
217     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
218       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
219     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
220       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
221     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
222       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
223     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
224       SP_VERB_INVALID, 0, 0},
225     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
226       SP_VERB_INVALID, 0, 0},
227     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
228       SP_VERB_INVALID, 0, 0},
229     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
230       SP_VERB_INVALID, 0, 0},
231     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
232       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
233     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
234 };
236 #define TOOLBAR_SLIDER_HINT "full"
238 static gchar const * ui_descr =
239         "<ui>"
240         "  <toolbar name='SelectToolbar'>"
241         "    <toolitem action='EditSelectAll' />"
242         "    <toolitem action='EditSelectAllInAllLayers' />"
243         "    <toolitem action='EditDeselect' />"
244         "    <separator />"
245         "    <toolitem action='ObjectRotate90CCW' />"
246         "    <toolitem action='ObjectRotate90' />"
247         "    <toolitem action='ObjectFlipHorizontally' />"
248         "    <toolitem action='ObjectFlipVertically' />"
249         "    <separator />"
250         "    <toolitem action='SelectionToBack' />"
251         "    <toolitem action='SelectionLower' />"
252         "    <toolitem action='SelectionRaise' />"
253         "    <toolitem action='SelectionToFront' />"
254         "    <separator />"
255         "    <toolitem action='XAction' />"
256         "    <toolitem action='YAction' />"
257         "    <toolitem action='WidthAction' />"
258         "    <toolitem action='LockAction' />"
259         "    <toolitem action='HeightAction' />"
260         "    <toolitem action='UnitsAction' />"
261         "    <separator />"
262         "    <toolitem action='transform_affect_label' />"
263         "    <toolitem action='transform_stroke' />"
264         "    <toolitem action='transform_corners' />"
265         "    <toolitem action='transform_gradient' />"
266         "    <toolitem action='transform_pattern' />"
267         "  </toolbar>"
269         "  <toolbar name='NodeToolbar'>"
270         "    <toolitem action='NodeInsertAction' />"
271         "    <toolitem action='NodeDeleteAction' />"
272         "    <separator />"
273         "    <toolitem action='NodeJoinAction' />"
274         "    <toolitem action='NodeBreakAction' />"
275         "    <separator />"
276         "    <toolitem action='NodeJoinSegmentAction' />"
277         "    <toolitem action='NodeDeleteSegmentAction' />"
278         "    <separator />"
279         "    <toolitem action='NodeCuspAction' />"
280         "    <toolitem action='NodeSmoothAction' />"
281         "    <toolitem action='NodeSymmetricAction' />"
282         "    <toolitem action='NodeAutoAction' />"
283         "    <separator />"
284         "    <toolitem action='NodeLineAction' />"
285         "    <toolitem action='NodeCurveAction' />"
286         "    <separator />"
287         "    <toolitem action='ObjectToPath' />"
288         "    <toolitem action='StrokeToPath' />"
289         "    <separator />"
290         "    <toolitem action='NodeXAction' />"
291         "    <toolitem action='NodeYAction' />"
292         "    <toolitem action='NodeUnitsAction' />"
293         "    <separator />"
294         "    <toolitem action='ObjectEditClipPathAction' />"
295         "    <toolitem action='ObjectEditMaskPathAction' />"
296         "    <toolitem action='EditNextLPEParameterAction' />"
297         "    <separator />"
298         "    <toolitem action='NodesShowHandlesAction' />"
299         "    <toolitem action='NodesShowHelperpath' />"
300         "  </toolbar>"
302         "  <toolbar name='TweakToolbar'>"
303         "    <toolitem action='TweakWidthAction' />"
304         "    <separator />"
305         "    <toolitem action='TweakForceAction' />"
306         "    <toolitem action='TweakPressureAction' />"
307         "    <separator />"
308         "    <toolitem action='TweakModeAction' />"
309         "    <separator />"
310         "    <toolitem action='TweakFidelityAction' />"
311         "    <separator />"
312         "    <toolitem action='TweakChannelsLabel' />"
313         "    <toolitem action='TweakDoH' />"
314         "    <toolitem action='TweakDoS' />"
315         "    <toolitem action='TweakDoL' />"
316         "    <toolitem action='TweakDoO' />"
317         "  </toolbar>"
319         "  <toolbar name='SprayToolbar'>"
320         "    <toolitem action='SprayModeAction' />"
321         "    <separator />"
322         "    <separator />"
323         "    <toolitem action='SprayWidthAction' />"
324         "    <toolitem action='SprayPressureAction' />"
325         "    <toolitem action='SprayPopulationAction' />"
326         "    <separator />"
327         "    <toolitem action='SprayRotationAction' />"
328         "    <toolitem action='SprayScaleAction' />"
329         "    <separator />"
330         "    <toolitem action='SprayStandard_deviationAction' />"
331         "    <toolitem action='SprayMeanAction' />"
332         "  </toolbar>"
334         "  <toolbar name='ZoomToolbar'>"
335         "    <toolitem action='ZoomIn' />"
336         "    <toolitem action='ZoomOut' />"
337         "    <separator />"
338         "    <toolitem action='Zoom1:0' />"
339         "    <toolitem action='Zoom1:2' />"
340         "    <toolitem action='Zoom2:1' />"
341         "    <separator />"
342         "    <toolitem action='ZoomSelection' />"
343         "    <toolitem action='ZoomDrawing' />"
344         "    <toolitem action='ZoomPage' />"
345         "    <toolitem action='ZoomPageWidth' />"
346         "    <separator />"
347         "    <toolitem action='ZoomPrev' />"
348         "    <toolitem action='ZoomNext' />"
349         "  </toolbar>"
351         "  <toolbar name='StarToolbar'>"
352         "    <separator />"
353         "    <toolitem action='StarStateAction' />"
354         "    <separator />"
355         "    <toolitem action='FlatAction' />"
356         "    <separator />"
357         "    <toolitem action='MagnitudeAction' />"
358         "    <toolitem action='SpokeAction' />"
359         "    <toolitem action='RoundednessAction' />"
360         "    <toolitem action='RandomizationAction' />"
361         "    <separator />"
362         "    <toolitem action='StarResetAction' />"
363         "  </toolbar>"
365         "  <toolbar name='RectToolbar'>"
366         "    <toolitem action='RectStateAction' />"
367         "    <toolitem action='RectWidthAction' />"
368         "    <toolitem action='RectHeightAction' />"
369         "    <toolitem action='RadiusXAction' />"
370         "    <toolitem action='RadiusYAction' />"
371         "    <toolitem action='RectUnitsAction' />"
372         "    <separator />"
373         "    <toolitem action='RectResetAction' />"
374         "  </toolbar>"
376         "  <toolbar name='3DBoxToolbar'>"
377         "    <toolitem action='3DBoxAngleXAction' />"
378         "    <toolitem action='3DBoxVPXStateAction' />"
379         "    <separator />"
380         "    <toolitem action='3DBoxAngleYAction' />"
381         "    <toolitem action='3DBoxVPYStateAction' />"
382         "    <separator />"
383         "    <toolitem action='3DBoxAngleZAction' />"
384         "    <toolitem action='3DBoxVPZStateAction' />"
385         "  </toolbar>"
387         "  <toolbar name='SpiralToolbar'>"
388         "    <toolitem action='SpiralStateAction' />"
389         "    <toolitem action='SpiralRevolutionAction' />"
390         "    <toolitem action='SpiralExpansionAction' />"
391         "    <toolitem action='SpiralT0Action' />"
392         "    <separator />"
393         "    <toolitem action='SpiralResetAction' />"
394         "  </toolbar>"
396         "  <toolbar name='PenToolbar'>"
397         "    <toolitem action='FreehandModeActionPen' />"
398         "    <separator />"
399         "    <toolitem action='SetPenShapeAction'/>"
400         "  </toolbar>"
402         "  <toolbar name='PencilToolbar'>"
403         "    <toolitem action='FreehandModeActionPencil' />"
404         "    <separator />"
405         "    <toolitem action='PencilToleranceAction' />"
406         "    <separator />"
407         "    <toolitem action='PencilResetAction' />"
408         "    <separator />"
409         "    <toolitem action='SetPencilShapeAction'/>"
410         "  </toolbar>"
412         "  <toolbar name='CalligraphyToolbar'>"
413         "    <separator />"
414         "    <toolitem action='SetProfileAction'/>"
415         "    <separator />"
416         "    <toolitem action='CalligraphyWidthAction' />"
417         "    <toolitem action='PressureAction' />"
418         "    <toolitem action='TraceAction' />"
419         "    <toolitem action='ThinningAction' />"
420         "    <separator />"
421         "    <toolitem action='AngleAction' />"
422         "    <toolitem action='TiltAction' />"
423         "    <toolitem action='FixationAction' />"
424         "    <separator />"
425         "    <toolitem action='CapRoundingAction' />"
426         "    <separator />"
427         "    <toolitem action='TremorAction' />"
428         "    <toolitem action='WiggleAction' />"
429         "    <toolitem action='MassAction' />"
430         "    <separator />"
431         "  </toolbar>"
433         "  <toolbar name='ArcToolbar'>"
434         "    <toolitem action='ArcStateAction' />"
435         "    <separator />"
436         "    <toolitem action='ArcStartAction' />"
437         "    <toolitem action='ArcEndAction' />"
438         "    <separator />"
439         "    <toolitem action='ArcOpenAction' />"
440         "    <separator />"
441         "    <toolitem action='ArcResetAction' />"
442         "    <separator />"
443         "  </toolbar>"
445         "  <toolbar name='PaintbucketToolbar'>"
446         "    <toolitem action='ChannelsAction' />"
447         "    <separator />"
448         "    <toolitem action='ThresholdAction' />"
449         "    <separator />"
450         "    <toolitem action='OffsetAction' />"
451         "    <toolitem action='PaintbucketUnitsAction' />"
452         "    <separator />"
453         "    <toolitem action='AutoGapAction' />"
454         "    <separator />"
455         "    <toolitem action='PaintbucketResetAction' />"
456         "  </toolbar>"
458         "  <toolbar name='EraserToolbar'>"
459         "    <toolitem action='EraserWidthAction' />"
460         "    <separator />"
461         "    <toolitem action='EraserModeAction' />"
462         "  </toolbar>"
464         "  <toolbar name='LPEToolToolbar'>"
465         "    <toolitem action='LPEToolModeAction' />"
466         "    <separator />"
467         "    <toolitem action='LPEShowBBoxAction' />"
468         "    <toolitem action='LPEBBoxFromSelectionAction' />"
469         "    <separator />"
470         "    <toolitem action='LPELineSegmentAction' />"
471         "    <separator />"
472         "    <toolitem action='LPEMeasuringAction' />"
473         "    <toolitem action='LPEToolUnitsAction' />"
474         "    <separator />"
475         "    <toolitem action='LPEOpenLPEDialogAction' />"
476         "  </toolbar>"
478         "  <toolbar name='DropperToolbar'>"
479         "    <toolitem action='DropperOpacityAction' />"
480         "    <toolitem action='DropperPickAlphaAction' />"
481         "    <toolitem action='DropperSetAlphaAction' />"
482         "  </toolbar>"
484         "  <toolbar name='ConnectorToolbar'>"
485         "    <toolitem action='ConnectorEditModeAction' />"
486         "    <toolitem action='ConnectorAvoidAction' />"
487         "    <toolitem action='ConnectorIgnoreAction' />"
488         "    <toolitem action='ConnectorOrthogonalAction' />"
489         "    <toolitem action='ConnectorCurvatureAction' />"
490         "    <toolitem action='ConnectorSpacingAction' />"
491         "    <toolitem action='ConnectorGraphAction' />"
492         "    <toolitem action='ConnectorLengthAction' />"
493         "    <toolitem action='ConnectorDirectedAction' />"
494         "    <toolitem action='ConnectorOverlapAction' />"
495         "    <toolitem action='ConnectorNewConnPointAction' />"
496         "    <toolitem action='ConnectorRemoveConnPointAction' />"
497         "  </toolbar>"
499         "</ui>"
502 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
504 void setup_snap_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
506 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
507 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
509 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
510 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
512 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
513 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
515 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
516                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
517                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
519 class VerbAction : public Gtk::Action {
520 public:
521     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
523     virtual ~VerbAction();
524     virtual void set_active(bool active = true);
526 protected:
527     virtual Gtk::Widget* create_menu_item_vfunc();
528     virtual Gtk::Widget* create_tool_item_vfunc();
530     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
531     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
533     virtual void on_activate();
535 private:
536     Inkscape::Verb* verb;
537     Inkscape::Verb* verb2;
538     Inkscape::UI::View::View *view;
539     GtkTooltips *tooltips;
540     bool active;
542     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
543 };
546 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
548     Glib::RefPtr<VerbAction> result;
549     SPAction *action = verb->get_action(view);
550     if ( action ) {
551         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
552         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
553     }
555     return result;
558 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
559     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
560     verb(verb),
561     verb2(verb2),
562     view(view),
563     tooltips(tooltips),
564     active(false)
568 VerbAction::~VerbAction()
572 Gtk::Widget* VerbAction::create_menu_item_vfunc()
574 // First call in to get the icon rendered if present in SVG
575     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
576     delete widget;
577     widget = 0;
579     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
580 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
581     return widg;
584 Gtk::Widget* VerbAction::create_tool_item_vfunc()
586 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
587     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/tools/small");
588     GtkWidget* toolbox = 0;
589     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
590                                                                           SP_BUTTON_TYPE_TOGGLE,
591                                                                           verb,
592                                                                           verb2,
593                                                                           view,
594                                                                           tooltips );
595     if ( active ) {
596         sp_button_toggle_set_down( SP_BUTTON(button), active);
597     }
598     gtk_widget_show_all( button );
599     Gtk::Widget* wrapped = Glib::wrap(button);
600     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
601     holder->add(*wrapped);
603 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
604     return holder;
607 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
609 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
610     Gtk::Action::connect_proxy_vfunc(proxy);
613 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
615 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
616     Gtk::Action::disconnect_proxy_vfunc(proxy);
619 void VerbAction::set_active(bool active)
621     this->active = active;
622     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
623     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
624         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
625         if (ti) {
626             // *should* have one child that is the SPButton
627             Gtk::Widget* child = ti->get_child();
628             if ( child && SP_IS_BUTTON(child->gobj()) ) {
629                 SPButton* button = SP_BUTTON(child->gobj());
630                 sp_button_toggle_set_down( button, active );
631             }
632         }
633     }
636 void VerbAction::on_activate()
638     if ( verb ) {
639         SPAction *action = verb->get_action(view);
640         if ( action ) {
641             sp_action_perform(action, 0);
642         }
643     }
646 /* Global text entry widgets necessary for update */
647 /* GtkWidget *dropper_rgb_entry,
648           *dropper_opacity_entry ; */
649 // should be made a private member once this is converted to class
651 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
652     connection->disconnect();
653     delete connection;
656 static void purge_repr_listener( GObject* obj, GObject* tbl )
658     (void)obj;
659     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
660     if (oldrepr) { // remove old listener
661         sp_repr_remove_listener_by_data(oldrepr, tbl);
662         Inkscape::GC::release(oldrepr);
663         oldrepr = 0;
664         g_object_set_data( tbl, "repr", NULL );
665     }
668 GtkWidget *
669 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
670                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
671                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
673     SPAction *action = verb->get_action(view);
674     if (!action) return NULL;
676     SPAction *doubleclick_action;
677     if (doubleclick_verb)
678         doubleclick_action = doubleclick_verb->get_action(view);
679     else
680         doubleclick_action = NULL;
682     /* fixme: Handle sensitive/unsensitive */
683     /* fixme: Implement sp_button_new_from_action */
684     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
685     gtk_widget_show(b);
688     unsigned int shortcut = sp_shortcut_get_primary(verb);
689     if (shortcut) {
690         gchar key[256];
691         sp_ui_shortcut_string(shortcut, key);
692         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
693         if ( t ) {
694             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
695         }
696         g_free(tip);
697     } else {
698         if ( t ) {
699             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
700         }
701     }
703     return b;
707 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
709     SPAction* targetAction = SP_ACTION(user_data);
710     if ( targetAction ) {
711         sp_action_perform( targetAction, NULL );
712     }
715 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
717     if ( data ) {
718         GtkAction* act = GTK_ACTION(data);
719         gtk_action_set_sensitive( act, sensitive );
720     }
723 static SPActionEventVector action_event_vector = {
724     {NULL},
725     NULL,
726     NULL,
727     sp_action_action_set_sensitive,
728     NULL,
729     NULL
730 };
732 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
734     GtkAction* act = 0;
736     SPAction* targetAction = verb->get_action(view);
737     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
738     act = GTK_ACTION(inky);
739     gtk_action_set_sensitive( act, targetAction->sensitive );
741     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
743     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
744     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
746     return act;
749 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
751     Inkscape::UI::View::View *view = desktop;
752     gint verbsToUse[] = {
753         // disabled until we have icons for them:
754         //find
755         //SP_VERB_EDIT_TILE,
756         //SP_VERB_EDIT_UNTILE,
757         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
758         SP_VERB_DIALOG_DISPLAY,
759         SP_VERB_DIALOG_FILL_STROKE,
760         SP_VERB_DIALOG_NAMEDVIEW,
761         SP_VERB_DIALOG_TEXT,
762         SP_VERB_DIALOG_XML_EDITOR,
763         SP_VERB_DIALOG_LAYERS,
764         SP_VERB_EDIT_CLONE,
765         SP_VERB_EDIT_COPY,
766         SP_VERB_EDIT_CUT,
767         SP_VERB_EDIT_DUPLICATE,
768         SP_VERB_EDIT_PASTE,
769         SP_VERB_EDIT_REDO,
770         SP_VERB_EDIT_UNDO,
771         SP_VERB_EDIT_UNLINK_CLONE,
772         SP_VERB_FILE_EXPORT,
773         SP_VERB_FILE_IMPORT,
774         SP_VERB_FILE_NEW,
775         SP_VERB_FILE_OPEN,
776         SP_VERB_FILE_PRINT,
777         SP_VERB_FILE_SAVE,
778         SP_VERB_OBJECT_TO_CURVE,
779         SP_VERB_SELECTION_GROUP,
780         SP_VERB_SELECTION_OUTLINE,
781         SP_VERB_SELECTION_UNGROUP,
782         SP_VERB_ZOOM_1_1,
783         SP_VERB_ZOOM_1_2,
784         SP_VERB_ZOOM_2_1,
785         SP_VERB_ZOOM_DRAWING,
786         SP_VERB_ZOOM_IN,
787         SP_VERB_ZOOM_NEXT,
788         SP_VERB_ZOOM_OUT,
789         SP_VERB_ZOOM_PAGE,
790         SP_VERB_ZOOM_PAGE_WIDTH,
791         SP_VERB_ZOOM_PREV,
792         SP_VERB_ZOOM_SELECTION,
793     };
795     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
797     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
798     Glib::RefPtr<Gtk::ActionGroup> mainActions;
799     if ( groups.find(desktop) != groups.end() ) {
800         mainActions = groups[desktop];
801     }
803     if ( !mainActions ) {
804         mainActions = Gtk::ActionGroup::create("main");
805         groups[desktop] = mainActions;
806     }
808     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
809         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
810         if ( verb ) {
811             if (!mainActions->get_action(verb->get_id())) {
812                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
813                 mainActions->add(Glib::wrap(act));
814             }
815         }
816     }
818     if ( !mainActions->get_action("ToolZoom") ) {
819         GtkTooltips *tt = gtk_tooltips_new();
820         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
821             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
822             if ( va ) {
823                 mainActions->add(va);
824                 if ( i == 0 ) {
825                     va->set_active(true);
826                 }
827             }
828         }
829     }
832     return mainActions;
836 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
838     gtk_widget_set_size_request( widget,
839                                  widget->allocation.width,
840                                  widget->allocation.height );
843 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
845     gtk_widget_set_size_request( widget, -1, -1 );
848 static GtkWidget* toolboxNewCommon( GtkWidget* tb, BarId id, GtkPositionType handlePos )
850     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
852     gtk_widget_set_sensitive(tb, FALSE);
854     GtkWidget *hb = gtk_handle_box_new();
855     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), handlePos);
856     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
857     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
859     gtk_container_add(GTK_CONTAINER(hb), tb);
860     gtk_widget_show(GTK_WIDGET(tb));
862     sigc::connection* conn = new sigc::connection;
863     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
865     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
866     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
868     gpointer val = GINT_TO_POINTER(id);
869     g_object_set_data(G_OBJECT(hb), BAR_ID_KEY, val);
871     return hb;
874 GtkWidget *ToolboxFactory::createToolToolbox()
876     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
878     return toolboxNewCommon( tb, BAR_TOOL, GTK_POS_TOP );
881 GtkWidget *ToolboxFactory::createAuxToolbox()
883     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
885     return toolboxNewCommon( tb, BAR_AUX, GTK_POS_LEFT );
888 //####################################
889 //# Commands Bar
890 //####################################
892 GtkWidget *ToolboxFactory::createCommandsToolbox()
894     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
896     return toolboxNewCommon( tb, BAR_COMMANDS, GTK_POS_LEFT );
899 GtkWidget *ToolboxFactory::createSnapToolbox()
901     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
903     return toolboxNewCommon( tb, BAR_SNAP, GTK_POS_LEFT );
906 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
907                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
908                                                        Glib::ustring const &path, gdouble def,
909                                                        GtkWidget *focusTarget,
910                                                        GtkWidget *us,
911                                                        GObject *dataKludge,
912                                                        gboolean altx, gchar const *altx_mark,
913                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
914                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
915                                                        void (*callback)(GtkAdjustment *, GObject *),
916                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
918     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
919     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
920                                                              lower, upper, step, page, 0 ) );
921     if (us) {
922         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
923     }
925     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
927     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
928     if ( shortLabel ) {
929         g_object_set( act, "short_label", shortLabel, NULL );
930     }
932     if ( (descrCount > 0) && descrLabels && descrValues ) {
933         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
934     }
936     if ( focusTarget ) {
937         ege_adjustment_action_set_focuswidget( act, focusTarget );
938     }
940     if ( altx && altx_mark ) {
941         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
942     }
944     if ( dataKludge ) {
945         // Rather lame, but it's the only place where we need to get the entry name
946         // but we don't have an Entry
947         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
948     }
950     // Using a cast just to make sure we pass in the right kind of function pointer
951     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
953     return act;
957 //####################################
958 //# node editing callbacks
959 //####################################
961 /**
962  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
963  */
964 static ShapeEditor *get_current_shape_editor()
966     if (!SP_ACTIVE_DESKTOP) {
967         return NULL;
968     }
970     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
972     if (!SP_IS_NODE_CONTEXT(event_context)) {
973         return NULL;
974     }
976     return event_context->shape_editor;
980 void
981 sp_node_path_edit_add(void)
983     ShapeEditor *shape_editor = get_current_shape_editor();
984     if (shape_editor) shape_editor->add_node();
987 void
988 sp_node_path_edit_delete(void)
990     ShapeEditor *shape_editor = get_current_shape_editor();
991     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
994 void
995 sp_node_path_edit_delete_segment(void)
997     ShapeEditor *shape_editor = get_current_shape_editor();
998     if (shape_editor) shape_editor->delete_segment();
1001 void
1002 sp_node_path_edit_break(void)
1004     ShapeEditor *shape_editor = get_current_shape_editor();
1005     if (shape_editor) shape_editor->break_at_nodes();
1008 void
1009 sp_node_path_edit_join(void)
1011     ShapeEditor *shape_editor = get_current_shape_editor();
1012     if (shape_editor) shape_editor->join_nodes();
1015 void
1016 sp_node_path_edit_join_segment(void)
1018     ShapeEditor *shape_editor = get_current_shape_editor();
1019     if (shape_editor) shape_editor->join_segments();
1022 void
1023 sp_node_path_edit_toline(void)
1025     ShapeEditor *shape_editor = get_current_shape_editor();
1026     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1029 void
1030 sp_node_path_edit_tocurve(void)
1032     ShapeEditor *shape_editor = get_current_shape_editor();
1033     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1036 void
1037 sp_node_path_edit_cusp(void)
1039     ShapeEditor *shape_editor = get_current_shape_editor();
1040     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1043 void
1044 sp_node_path_edit_smooth(void)
1046     ShapeEditor *shape_editor = get_current_shape_editor();
1047     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1050 void
1051 sp_node_path_edit_symmetrical(void)
1053     ShapeEditor *shape_editor = get_current_shape_editor();
1054     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1057 void
1058 sp_node_path_edit_auto(void)
1060     ShapeEditor *shape_editor = get_current_shape_editor();
1061     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1064 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1065     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1066     bool show = gtk_toggle_action_get_active( act );
1067     prefs->setBool("/tools/nodes/show_handles",  show);
1068     ShapeEditor *shape_editor = get_current_shape_editor();
1069     if (shape_editor) shape_editor->show_handles(show);
1072 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1073     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1074     bool show = gtk_toggle_action_get_active( act );
1075     prefs->setBool("/tools/nodes/show_helperpath",  show);
1076     ShapeEditor *shape_editor = get_current_shape_editor();
1077     if (shape_editor) shape_editor->show_helperpath(show);
1080 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1081     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1084 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1085     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1088 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1089     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1092 /* is called when the node selection is modified */
1093 static void
1094 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1096     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1097     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1098     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1099     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1101     // quit if run by the attr_changed listener
1102     if (g_object_get_data( tbl, "freeze" )) {
1103         return;
1104     }
1106     // in turn, prevent listener from responding
1107     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1109     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1110     SPUnit const *unit = tracker->getActiveUnit();
1112     ShapeEditor *shape_editor = get_current_shape_editor();
1113     if (shape_editor && shape_editor->has_nodepath()) {
1114         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1115         int n_selected = 0;
1116         if (nodepath) {
1117             n_selected = nodepath->numSelected();
1118         }
1120         if (n_selected == 0) {
1121             gtk_action_set_sensitive(xact, FALSE);
1122             gtk_action_set_sensitive(yact, FALSE);
1123         } else {
1124             gtk_action_set_sensitive(xact, TRUE);
1125             gtk_action_set_sensitive(yact, TRUE);
1126             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1127             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1129             if (n_selected == 1) {
1130                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1131                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1132                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1133                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1134                 }
1135             } else {
1136                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1137                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1138                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1139                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1140                        selected nodes don't coincide (in this case we use the coordinates of the center
1141                        of the bounding box). So the entries are never set to zero. */
1142                     // FIXME: Maybe we should clear the entry if several nodes are selected
1143                     //        instead of providing a kind of average value
1144                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1145                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1146                 }
1147             }
1148         }
1149     } else {
1150         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1151         gtk_action_set_sensitive(xact, FALSE);
1152         gtk_action_set_sensitive(yact, FALSE);
1153     }
1155     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1158 static void
1159 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1161     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1162     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1164     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1165     SPUnit const *unit = tracker->getActiveUnit();
1167     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1168         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1169     }
1171     // quit if run by the attr_changed listener
1172     if (g_object_get_data( tbl, "freeze" )) {
1173         return;
1174     }
1176     // in turn, prevent listener from responding
1177     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1179     ShapeEditor *shape_editor = get_current_shape_editor();
1180     if (shape_editor && shape_editor->has_nodepath()) {
1181         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1182         if (!strcmp(value_name, "x")) {
1183             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1184         }
1185         if (!strcmp(value_name, "y")) {
1186             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1187         }
1188     }
1190     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1193 static void
1194 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1196     sp_node_path_value_changed(adj, tbl, "x");
1199 static void
1200 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1202     sp_node_path_value_changed(adj, tbl, "y");
1205 void
1206 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1208     {
1209     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1210     SPItem *item = selection->singleItem();
1211     if (item && SP_IS_LPE_ITEM(item)) {
1212        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1213            gtk_action_set_sensitive(w, TRUE);
1214        } else {
1215            gtk_action_set_sensitive(w, FALSE);
1216        }
1217     } else {
1218        gtk_action_set_sensitive(w, FALSE);
1219     }
1220     }
1222     {
1223     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1224     SPItem *item = selection->singleItem();
1225     if (item && item->clip_ref && item->clip_ref->getObject()) {
1226        gtk_action_set_sensitive(w, TRUE);
1227     } else {
1228        gtk_action_set_sensitive(w, FALSE);
1229     }
1230     }
1232     {
1233     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1234     SPItem *item = selection->singleItem();
1235     if (item && item->mask_ref && item->mask_ref->getObject()) {
1236        gtk_action_set_sensitive(w, TRUE);
1237     } else {
1238        gtk_action_set_sensitive(w, FALSE);
1239     }
1240     }
1243 void
1244 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1246     sp_node_toolbox_sel_changed (selection, tbl);
1251 //################################
1252 //##    Node Editing Toolbox    ##
1253 //################################
1255 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1257     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1258     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1259     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1260     g_object_set_data( holder, "tracker", tracker );
1262     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
1264     {
1265         InkAction* inky = ink_action_new( "NodeInsertAction",
1266                                           _("Insert node"),
1267                                           _("Insert new nodes into selected segments"),
1268                                           INKSCAPE_ICON_NODE_ADD,
1269                                           secondarySize );
1270         g_object_set( inky, "short_label", _("Insert"), NULL );
1271         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1272         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1273     }
1275     {
1276         InkAction* inky = ink_action_new( "NodeDeleteAction",
1277                                           _("Delete node"),
1278                                           _("Delete selected nodes"),
1279                                           INKSCAPE_ICON_NODE_DELETE,
1280                                           secondarySize );
1281         g_object_set( inky, "short_label", _("Delete"), NULL );
1282         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1283         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1284     }
1286     {
1287         InkAction* inky = ink_action_new( "NodeJoinAction",
1288                                           _("Join endnodes"),
1289                                           _("Join selected endnodes"),
1290                                           INKSCAPE_ICON_NODE_JOIN,
1291                                           secondarySize );
1292         g_object_set( inky, "short_label", _("Join"), NULL );
1293         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1294         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1295     }
1297     {
1298         InkAction* inky = ink_action_new( "NodeBreakAction",
1299                                           _("Break nodes"),
1300                                           _("Break path at selected nodes"),
1301                                           INKSCAPE_ICON_NODE_BREAK,
1302                                           secondarySize );
1303         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1304         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1305     }
1308     {
1309         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1310                                           _("Join with segment"),
1311                                           _("Join selected endnodes with a new segment"),
1312                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1313                                           secondarySize );
1314         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1315         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1316     }
1318     {
1319         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1320                                           _("Delete segment"),
1321                                           _("Delete segment between two non-endpoint nodes"),
1322                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1323                                           secondarySize );
1324         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1325         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1326     }
1328     {
1329         InkAction* inky = ink_action_new( "NodeCuspAction",
1330                                           _("Node Cusp"),
1331                                           _("Make selected nodes corner"),
1332                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1333                                           secondarySize );
1334         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1335         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1336     }
1338     {
1339         InkAction* inky = ink_action_new( "NodeSmoothAction",
1340                                           _("Node Smooth"),
1341                                           _("Make selected nodes smooth"),
1342                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1343                                           secondarySize );
1344         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1345         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1346     }
1348     {
1349         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1350                                           _("Node Symmetric"),
1351                                           _("Make selected nodes symmetric"),
1352                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1353                                           secondarySize );
1354         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1355         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1356     }
1358     {
1359         InkAction* inky = ink_action_new( "NodeAutoAction",
1360                                           _("Node Auto"),
1361                                           _("Make selected nodes auto-smooth"),
1362                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1363                                           secondarySize );
1364         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1365         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1366     }
1368     {
1369         InkAction* inky = ink_action_new( "NodeLineAction",
1370                                           _("Node Line"),
1371                                           _("Make selected segments lines"),
1372                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1373                                           secondarySize );
1374         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1375         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1376     }
1378     {
1379         InkAction* inky = ink_action_new( "NodeCurveAction",
1380                                           _("Node Curve"),
1381                                           _("Make selected segments curves"),
1382                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1383                                           secondarySize );
1384         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1385         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1386     }
1388     {
1389         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1390                                                       _("Show Handles"),
1391                                                       _("Show the Bezier handles of selected nodes"),
1392                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1393                                                       Inkscape::ICON_SIZE_DECORATION );
1394         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1395         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1396         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1397     }
1399     {
1400         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1401                                                       _("Show Outline"),
1402                                                       _("Show the outline of the path"),
1403                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1404                                                       Inkscape::ICON_SIZE_DECORATION );
1405         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1406         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1407         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1408     }
1410     {
1411         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1412                                           _("Next path effect parameter"),
1413                                           _("Show next path effect parameter for editing"),
1414                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1415                                           Inkscape::ICON_SIZE_DECORATION );
1416         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1418         g_object_set_data( holder, "nodes_lpeedit", inky);
1419     }
1421     {
1422         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1423                                           _("Edit clipping path"),
1424                                           _("Edit the clipping path of the object"),
1425                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1426                                           Inkscape::ICON_SIZE_DECORATION );
1427         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1429         g_object_set_data( holder, "nodes_clippathedit", inky);
1430     }
1432     {
1433         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1434                                           _("Edit mask path"),
1435                                           _("Edit the mask of the object"),
1436                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1437                                           Inkscape::ICON_SIZE_DECORATION );
1438         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1439         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1440         g_object_set_data( holder, "nodes_maskedit", inky);
1441     }
1443     /* X coord of selected node(s) */
1444     {
1445         EgeAdjustmentAction* eact = 0;
1446         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1447         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1448         eact = create_adjustment_action( "NodeXAction",
1449                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1450                                          "/tools/nodes/Xcoord", 0,
1451                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1452                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1453                                          labels, values, G_N_ELEMENTS(labels),
1454                                          sp_node_path_x_value_changed );
1455         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1456         g_object_set_data( holder, "nodes_x_action", eact );
1457         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1458         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1459     }
1461     /* Y coord of selected node(s) */
1462     {
1463         EgeAdjustmentAction* eact = 0;
1464         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1465         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1466         eact = create_adjustment_action( "NodeYAction",
1467                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1468                                          "/tools/nodes/Ycoord", 0,
1469                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1470                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1471                                          labels, values, G_N_ELEMENTS(labels),
1472                                          sp_node_path_y_value_changed );
1473         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1474         g_object_set_data( holder, "nodes_y_action", eact );
1475         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1476         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1477     }
1479     // add the units menu
1480     {
1481         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1482         gtk_action_group_add_action( mainActions, act );
1483     }
1486     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1488     //watch selection
1489     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1491     sigc::connection *c_selection_changed =
1492         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1493                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1494     pool->add_connection ("selection-changed", c_selection_changed);
1496     sigc::connection *c_selection_modified =
1497         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1498                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1499     pool->add_connection ("selection-modified", c_selection_modified);
1501     sigc::connection *c_subselection_changed =
1502         new sigc::connection (desktop->connectToolSubselectionChanged
1503                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1504     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1506     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1508     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1509 } // end of sp_node_toolbox_prep()
1512 //########################
1513 //##    Zoom Toolbox    ##
1514 //########################
1516 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1518     // no custom GtkAction setup needed
1519 } // end of sp_zoom_toolbox_prep()
1521 void ToolboxFactory::setToolboxDesktop(GtkWidget *toolbox, SPDesktop *desktop)
1523     sigc::connection *conn = static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1524                                                                               "event_context_connection"));
1526     BarId id = static_cast<BarId>( GPOINTER_TO_INT(g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY)) );
1528     SetupFunction setup_func = 0;
1529     UpdateFunction update_func = 0;
1531     switch (id) {
1532         case BAR_TOOL:
1533             setup_func = setup_tool_toolbox;
1534             update_func = update_tool_toolbox;
1535             break;
1537         case BAR_AUX:
1538             toolbox = gtk_bin_get_child(GTK_BIN(toolbox));
1539             setup_func = setup_aux_toolbox;
1540             update_func = update_aux_toolbox;
1541             break;
1543         case BAR_COMMANDS:
1544             setup_func = setup_commands_toolbox;
1545             update_func = update_commands_toolbox;
1546             break;
1548         case BAR_SNAP:
1549             setup_func = setup_snap_toolbox;
1550             update_func = updateSnapToolbox;
1551             break;
1552         default:
1553             g_warning("Unexpected toolbox id encountered.");
1554     }
1556     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1557     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1559     if (old_desktop) {
1560         GList *children, *iter;
1562         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1563         for ( iter = children ; iter ; iter = iter->next ) {
1564             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1565         }
1566         g_list_free(children);
1567     }
1569     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1571     if (desktop && setup_func && update_func) {
1572         gtk_widget_set_sensitive(toolbox, TRUE);
1573         setup_func(toolbox, desktop);
1574         update_func(desktop, desktop->event_context, toolbox);
1575         *conn = desktop->connectEventContextChanged(sigc::bind (sigc::ptr_fun(update_func), toolbox));
1576     } else {
1577         gtk_widget_set_sensitive(toolbox, FALSE);
1578     }
1580 } // end of sp_toolbox_set_desktop()
1583 static void setupToolboxCommon( GtkWidget *toolbox,
1584                                 SPDesktop *desktop,
1585                                 gchar const *descr,
1586                                 gchar const* toolbarName,
1587                                 gchar const* sizePref )
1589     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1590     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1592     GtkUIManager* mgr = gtk_ui_manager_new();
1593     GError* errVal = 0;
1595     GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
1597     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1598     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1600     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, toolbarName );
1601     if ( prefs->getBool("/toolbox/icononly", true) ) {
1602         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1603     }
1605     Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize(sizePref);
1606     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1608     if (GTK_IS_HANDLE_BOX(toolbox)) {
1609         // g_message("GRABBING ORIENTATION   [%s]", toolbarName);
1610         GtkPositionType pos = gtk_handle_box_get_handle_position(GTK_HANDLE_BOX(toolbox));
1611         orientation = ((pos == GTK_POS_LEFT) || (pos == GTK_POS_RIGHT)) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1612     }
1613     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), orientation);
1614     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1616     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1618     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1619     if ( child ) {
1620         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1621     }
1623     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1626 void ToolboxFactory::setOrientation(GtkWidget* toolbox, GtkOrientation orientation)
1628     //g_message("Set orientation for %p to be %d", toolbox, orientation);
1629     //GType type = GTK_WIDGET_TYPE(toolbox);
1630     //g_message("        [%s]", g_type_name(type));
1631     //g_message("             %p", g_object_get_data(G_OBJECT(toolbox), BAR_ID_KEY));
1633     if (GTK_IS_BIN(toolbox)) {
1634         //g_message("            is a BIN");
1635         GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1636         if (child) {
1637             //GType type2 = GTK_WIDGET_TYPE(child);
1638             //g_message("            child    [%s]", g_type_name(type2));
1640             if (GTK_IS_BOX(child)) {
1641                 //g_message("                is a BOX");
1643                 GList* children = gtk_container_get_children(GTK_CONTAINER(child));
1644                 if (children && children->data) {
1645                     //GtkWidget* child2 = GTK_WIDGET(children->data);
1646                     //GType type3 = GTK_WIDGET_TYPE(child2);
1647                     //g_message("                child    [%s]", g_type_name(type3));
1648                     g_message("need to add dynamic switch");
1650                     g_list_free(children);
1651                 } else {
1652                     //g_message("                    has no children %p", children);
1653                     // The call is being made before the toolbox proper has been setup.
1654                     if (GTK_IS_HANDLE_BOX(toolbox)) {
1655                         GtkPositionType pos = (orientation == GTK_ORIENTATION_HORIZONTAL) ? GTK_POS_LEFT : GTK_POS_TOP;
1656                         gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(toolbox), pos);
1657                         //g_message("Setting position");
1658                     }
1659                 }
1660             }
1661         }
1662     }
1668 static void
1669 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1671     gchar const * descr =
1672         "<ui>"
1673         "  <toolbar name='ToolToolbar'>"
1674         "    <toolitem action='ToolSelector' />"
1675         "    <toolitem action='ToolNode' />"
1676         "    <toolitem action='ToolTweak' />"
1677         "    <toolitem action='ToolSpray' />"
1678         "    <toolitem action='ToolZoom' />"
1679         "    <toolitem action='ToolRect' />"
1680         "    <toolitem action='Tool3DBox' />"
1681         "    <toolitem action='ToolArc' />"
1682         "    <toolitem action='ToolStar' />"
1683         "    <toolitem action='ToolSpiral' />"
1684         "    <toolitem action='ToolPencil' />"
1685         "    <toolitem action='ToolPen' />"
1686         "    <toolitem action='ToolCalligraphic' />"
1687         "    <toolitem action='ToolEraser' />"
1688 //        "    <toolitem action='ToolLPETool' />"
1689         "    <toolitem action='ToolPaintBucket' />"
1690         "    <toolitem action='ToolText' />"
1691         "    <toolitem action='ToolConnector' />"
1692         "    <toolitem action='ToolGradient' />"
1693         "    <toolitem action='ToolDropper' />"
1694         "  </toolbar>"
1695         "</ui>";
1697     setupToolboxCommon( toolbox, desktop, descr,
1698                         "/ui/ToolToolbar",
1699                         "/toolbox/tools/small");
1702 static void
1703 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1705     gchar const *const tname = ( eventcontext
1706                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1707                                  : NULL );
1708     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1710     for (int i = 0 ; tools[i].type_name ; i++ ) {
1711         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1712         if ( act ) {
1713             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1714             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1715             if ( verbAct ) {
1716                 verbAct->set_active(setActive);
1717             }
1718         }
1719     }
1722 static void
1723 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1725     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1726     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1727     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1728     GtkUIManager* mgr = gtk_ui_manager_new();
1729     GError* errVal = 0;
1730     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1731     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1733     std::map<std::string, GtkWidget*> dataHolders;
1735     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1736         if ( aux_toolboxes[i].prep_func ) {
1737             // converted to GtkActions and UIManager
1739             GtkWidget* kludge = gtk_toolbar_new();
1740             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1741             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1742             dataHolders[aux_toolboxes[i].type_name] = kludge;
1743             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1744         } else {
1746             GtkWidget *sub_toolbox = 0;
1747             if (aux_toolboxes[i].create_func == NULL) {
1748                 sub_toolbox = sp_empty_toolbox_new(desktop);
1749             } else {
1750                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1751             }
1753             gtk_size_group_add_widget( grouper, sub_toolbox );
1755             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1756             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1758         }
1759     }
1761     // Second pass to create toolbars *after* all GtkActions are created
1762     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1763         if ( aux_toolboxes[i].prep_func ) {
1764             // converted to GtkActions and UIManager
1766             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1768             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1769             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1771             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1772             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1773             g_free( tmp );
1774             tmp = 0;
1776             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1777                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1778             }
1780             Inkscape::IconSize toolboxSize = ToolboxFactory::prefToSize("/toolbox/small");
1781             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1783             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1785             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1786                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1787                 swatch->setDesktop( desktop );
1788                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1789                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1790                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1791                 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 );
1792             }
1794             gtk_widget_show_all( holder );
1795             sp_set_font_size_smaller( holder );
1797             gtk_size_group_add_widget( grouper, holder );
1799             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1800             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1801         }
1802     }
1804     g_object_unref( G_OBJECT(grouper) );
1807 static void
1808 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1810     gchar const *tname = ( eventcontext
1811                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1812                            : NULL );
1813     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1814         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1815         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1816             gtk_widget_show_all(sub_toolbox);
1817             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1818         } else {
1819             gtk_widget_hide(sub_toolbox);
1820         }
1821     }
1824 static void
1825 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1827     gchar const * descr =
1828         "<ui>"
1829         "  <toolbar name='CommandsToolbar'>"
1830         "    <toolitem action='FileNew' />"
1831         "    <toolitem action='FileOpen' />"
1832         "    <toolitem action='FileSave' />"
1833         "    <toolitem action='FilePrint' />"
1834         "    <separator />"
1835         "    <toolitem action='FileImport' />"
1836         "    <toolitem action='FileExport' />"
1837         "    <separator />"
1838         "    <toolitem action='EditUndo' />"
1839         "    <toolitem action='EditRedo' />"
1840         "    <separator />"
1841         "    <toolitem action='EditCopy' />"
1842         "    <toolitem action='EditCut' />"
1843         "    <toolitem action='EditPaste' />"
1844         "    <separator />"
1845         "    <toolitem action='ZoomSelection' />"
1846         "    <toolitem action='ZoomDrawing' />"
1847         "    <toolitem action='ZoomPage' />"
1848         "    <separator />"
1849         "    <toolitem action='EditDuplicate' />"
1850         "    <toolitem action='EditClone' />"
1851         "    <toolitem action='EditUnlinkClone' />"
1852         "    <separator />"
1853         "    <toolitem action='SelectionGroup' />"
1854         "    <toolitem action='SelectionUnGroup' />"
1855         "    <separator />"
1856         "    <toolitem action='DialogFillStroke' />"
1857         "    <toolitem action='DialogText' />"
1858         "    <toolitem action='DialogLayers' />"
1859         "    <toolitem action='DialogXMLEditor' />"
1860         "    <toolitem action='DialogAlignDistribute' />"
1861         "    <separator />"
1862         "    <toolitem action='DialogPreferences' />"
1863         "    <toolitem action='DialogDocumentProperties' />"
1864         "  </toolbar>"
1865         "</ui>";
1867     setupToolboxCommon( toolbox, desktop, descr,
1868                         "/ui/CommandsToolbar",
1869                         "/toolbox/small" );
1872 static void
1873 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1877 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1879     if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1880         return;
1881     }
1883     gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1884     g_assert(ptr != NULL);
1886     SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1887     SPNamedView *nv = sp_desktop_namedview(dt);
1888     SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1890     if (dt == NULL || nv == NULL) {
1891         g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1892         return;
1893     }
1895     Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1897     if (repr == NULL) {
1898         g_warning("This namedview doesn't have a xml representation attached!");
1899         return;
1900     }
1902     bool saved = sp_document_get_undo_sensitive(doc);
1903     sp_document_set_undo_sensitive(doc, false);
1905     bool v = false;
1906     SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1908     switch (attr) {
1909         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1910             dt->toggleSnapGlobal();
1911             break;
1912         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1913             v = nv->snap_manager.snapprefs.getSnapModeBBox();
1914             sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1915             break;
1916         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1917             v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1918             sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1919             break;
1920         case SP_ATTR_INKSCAPE_BBOX_NODES:
1921             v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1922             sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1923             break;
1924         case SP_ATTR_INKSCAPE_SNAP_NODES:
1925             v = nv->snap_manager.snapprefs.getSnapModeNode();
1926             sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1927             break;
1928         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1929             v = nv->snap_manager.snapprefs.getSnapToItemPath();
1930             sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1931             break;
1932         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1933             v = nv->snap_manager.snapprefs.getSnapToItemNode();
1934             sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1935             break;
1936         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1937             v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1938             sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1939             break;
1940         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1941             v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1942             sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1943             break;
1944         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1945             v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1946             sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1947             break;
1948         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1949             v = nv->snap_manager.snapprefs.getSnapToGrids();
1950             sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1951             break;
1952         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1953             v = nv->snap_manager.snapprefs.getSnapToGuides();
1954             sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1955             break;
1956         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1957             v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1958             sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1959             break;
1960             /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1961               v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1962               sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1963               break;*/
1964         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1965             v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1966             sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1967             break;
1968         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1969             v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1970             sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1971             break;
1972         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1973             v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1974             sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1975             break;
1976         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1977             v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1978             sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1979             break;
1980         default:
1981             g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1982             break;
1983     }
1985     // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1986     doc->setModifiedSinceSave();
1988     sp_document_set_undo_sensitive(doc, saved);
1991 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1993     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1995     gchar const * descr =
1996         "<ui>"
1997         "  <toolbar name='SnapToolbar'>"
1998         "    <toolitem action='ToggleSnapGlobal' />"
1999         "    <separator />"
2000         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2001         "    <toolitem action='ToggleSnapToBBoxPath' />"
2002         "    <toolitem action='ToggleSnapToBBoxNode' />"
2003         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2004         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2005         "    <separator />"
2006         "    <toolitem action='ToggleSnapFromNode' />"
2007         "    <toolitem action='ToggleSnapToItemPath' />"
2008         "    <toolitem action='ToggleSnapToPathIntersections' />"
2009         "    <toolitem action='ToggleSnapToItemNode' />"
2010         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2011         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2012         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2013         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2014         "    <separator />"
2015         "    <toolitem action='ToggleSnapToPageBorder' />"
2016         "    <toolitem action='ToggleSnapToGrids' />"
2017         "    <toolitem action='ToggleSnapToGuides' />"
2018         //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2019         "  </toolbar>"
2020         "</ui>";
2022     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2024     {
2025         InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2026                                                      _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2027                                                      SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2029         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2030         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2031     }
2033     {
2034         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2035                                                      _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2036                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2038         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2039         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2040     }
2042     {
2043         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2044                                                      _("Bounding box edges"), _("Snap to edges of a bounding box"),
2045                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2047         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2048         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2049     }
2051     {
2052         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2053                                                      _("Bounding box corners"), _("Snap to bounding box corners"),
2054                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2056         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2057         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2058     }
2060     {
2061         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2062                                                      _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2063                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2064                                                      SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2066         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2067         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2068     }
2070     {
2071         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2072                                                      _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2073                                                      INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2075         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2076         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2077     }
2079     {
2080         InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2081                                                      _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2083         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2084         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2085     }
2087     {
2088         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2089                                                      _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2090                                                      SP_ATTR_INKSCAPE_OBJECT_PATHS);
2092         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2093         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2094     }
2096     {
2097         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2098                                                      _("Path intersections"), _("Snap to path intersections"),
2099                                                      INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2101         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2102         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2103     }
2105     {
2106         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2107                                                      _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2108                                                      SP_ATTR_INKSCAPE_OBJECT_NODES);
2110         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2111         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2112     }
2114     {
2115         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2116                                                      _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2117                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2119         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2120         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2121     }
2123     {
2124         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2125                                                      _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2126                                                      INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2128         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2129         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2130     }
2132     {
2133         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2134                                                      _("Object Centers"), _("Snap from and to centers of objects"),
2135                                                      INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2137         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2138         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2139     }
2141     {
2142         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2143                                                      _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2144                                                      INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
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("ToggleSnapToPageBorder",
2152                                                      _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2153                                                      secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
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("ToggleSnapToGrids",
2161                                                      _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2162                                                      SP_ATTR_INKSCAPE_SNAP_GRIDS);
2164         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2165         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2166     }
2168     {
2169         InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2170                                                      _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2171                                                      SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2173         gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2174         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2175     }
2177     /*{
2178       InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2179       _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2180       INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2181       SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2183       gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2184       g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2185       }*/
2187     setupToolboxCommon( toolbox, desktop, descr,
2188                         "/ui/SnapToolbar",
2189                         "/toolbox/secondary" );
2192 void ToolboxFactory::updateSnapToolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2194     g_assert(desktop != NULL);
2195     g_assert(toolbox != NULL);
2197     SPNamedView *nv = sp_desktop_namedview(desktop);
2198     if (nv == NULL) {
2199         g_warning("Namedview cannot be retrieved (in updateSnapToolbox)!");
2200         return;
2201     }
2203     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2205     Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2206     Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2207     Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2208     Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2209     Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2210     Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2211     Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2212     Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2213     Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2214     Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2215     Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2216     Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2217     Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2218     Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2219     Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2220     //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2221     Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2222     Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2225     if (!act1) {
2226         return; // The snap actions haven't been defined yet (might be the case during startup)
2227     }
2229     // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2230     // changes in our document because we're only updating the UI;
2231     // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2232     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2234     bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2235     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2237     bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2238     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2239     gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2241     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2242     gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2243     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2244     gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2245     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2246     gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2247     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2248     gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2250     bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2251     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2252     gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2254     bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2255     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2256     gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2257     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2258     gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2259     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2260     gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2261     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2262     gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2263     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2264     gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2265     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2266     gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2267     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2268     gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2270     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2271     gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2272     //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2273     //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2275     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2276     gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2277     gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2278     gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2281     g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2284 void ToolboxFactory::showAuxToolbox(GtkWidget *toolbox_toplevel)
2286     gtk_widget_show(toolbox_toplevel);
2287     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2289     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2290     if (!shown_toolbox) {
2291         return;
2292     }
2293     gtk_widget_show(toolbox);
2295     gtk_widget_show_all(shown_toolbox);
2298 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop)
2300     GtkWidget *tbl = gtk_toolbar_new();
2301     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2302     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2304     gtk_widget_show_all(tbl);
2305     sp_set_font_size_smaller (tbl);
2307     return tbl;
2310 #define MODE_LABEL_WIDTH 70
2312 //########################
2313 //##       Star         ##
2314 //########################
2316 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2318     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2320     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2321         // do not remember prefs if this call is initiated by an undo change, because undoing object
2322         // creation sets bogus values to its attributes before it is deleted
2323         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2324         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2325     }
2327     // quit if run by the attr_changed listener
2328     if (g_object_get_data( dataKludge, "freeze" )) {
2329         return;
2330     }
2332     // in turn, prevent listener from responding
2333     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2335     bool modmade = false;
2337     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2338     GSList const *items = selection->itemList();
2339     for (; items != NULL; items = items->next) {
2340         if (SP_IS_STAR((SPItem *) items->data)) {
2341             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2342             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2343             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2344                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2345                                     + M_PI / (gint)adj->value));
2346             SP_OBJECT((SPItem *) items->data)->updateRepr();
2347             modmade = true;
2348         }
2349     }
2350     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2351                                    _("Star: Change number of corners"));
2353     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2356 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2358     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2360     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2361         if (!IS_NAN(adj->value)) {
2362             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2363             prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2364         }
2365     }
2367     // quit if run by the attr_changed listener
2368     if (g_object_get_data( dataKludge, "freeze" )) {
2369         return;
2370     }
2372     // in turn, prevent listener from responding
2373     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2375     bool modmade = false;
2376     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2377     GSList const *items = selection->itemList();
2378     for (; items != NULL; items = items->next) {
2379         if (SP_IS_STAR((SPItem *) items->data)) {
2380             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2382             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2383             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2384             if (r2 < r1) {
2385                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2386             } else {
2387                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2388             }
2390             SP_OBJECT((SPItem *) items->data)->updateRepr();
2391             modmade = true;
2392         }
2393     }
2395     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2396                                    _("Star: Change spoke ratio"));
2398     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2401 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2403     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2404     bool flat = ege_select_one_action_get_active( act ) == 0;
2406     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2407         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2408         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2409     }
2411     // quit if run by the attr_changed listener
2412     if (g_object_get_data( dataKludge, "freeze" )) {
2413         return;
2414     }
2416     // in turn, prevent listener from responding
2417     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2419     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2420     GSList const *items = selection->itemList();
2421     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2422     bool modmade = false;
2424     if ( prop_action ) {
2425         gtk_action_set_sensitive( prop_action, !flat );
2426     }
2428     for (; items != NULL; items = items->next) {
2429         if (SP_IS_STAR((SPItem *) items->data)) {
2430             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2431             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2432             SP_OBJECT((SPItem *) items->data)->updateRepr();
2433             modmade = true;
2434         }
2435     }
2437     if (modmade) {
2438         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2439                          flat ? _("Make polygon") : _("Make star"));
2440     }
2442     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2445 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2447     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2449     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2450         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2451         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2452     }
2454     // quit if run by the attr_changed listener
2455     if (g_object_get_data( dataKludge, "freeze" )) {
2456         return;
2457     }
2459     // in turn, prevent listener from responding
2460     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2462     bool modmade = false;
2464     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2465     GSList const *items = selection->itemList();
2466     for (; items != NULL; items = items->next) {
2467         if (SP_IS_STAR((SPItem *) items->data)) {
2468             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2469             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2470             SP_OBJECT(items->data)->updateRepr();
2471             modmade = true;
2472         }
2473     }
2474     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2475                                    _("Star: Change rounding"));
2477     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2480 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2482     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2484     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2485         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2486         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2487     }
2489     // quit if run by the attr_changed listener
2490     if (g_object_get_data( dataKludge, "freeze" )) {
2491         return;
2492     }
2494     // in turn, prevent listener from responding
2495     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2497     bool modmade = false;
2499     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2500     GSList const *items = selection->itemList();
2501     for (; items != NULL; items = items->next) {
2502         if (SP_IS_STAR((SPItem *) items->data)) {
2503             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2504             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2505             SP_OBJECT(items->data)->updateRepr();
2506             modmade = true;
2507         }
2508     }
2509     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2510                                    _("Star: Change randomization"));
2512     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2516 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2517                                        gchar const */*old_value*/, gchar const */*new_value*/,
2518                                        bool /*is_interactive*/, gpointer data)
2520     GtkWidget *tbl = GTK_WIDGET(data);
2522     // quit if run by the _changed callbacks
2523     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2524         return;
2525     }
2527     // in turn, prevent callbacks from responding
2528     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2530     GtkAdjustment *adj = 0;
2532     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2533     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2535     if (!strcmp(name, "inkscape:randomized")) {
2536         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2537         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2538     } else if (!strcmp(name, "inkscape:rounded")) {
2539         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2540         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2541     } else if (!strcmp(name, "inkscape:flatsided")) {
2542         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2543         char const *flatsides = repr->attribute("inkscape:flatsided");
2544         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2545         if ( flatsides && !strcmp(flatsides,"false") ) {
2546             ege_select_one_action_set_active( flat_action, 1 );
2547             gtk_action_set_sensitive( prop_action, TRUE );
2548         } else {
2549             ege_select_one_action_set_active( flat_action, 0 );
2550             gtk_action_set_sensitive( prop_action, FALSE );
2551         }
2552     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2553         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2554         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2555         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2556         if (r2 < r1) {
2557             gtk_adjustment_set_value(adj, r2/r1);
2558         } else {
2559             gtk_adjustment_set_value(adj, r1/r2);
2560         }
2561     } else if (!strcmp(name, "sodipodi:sides")) {
2562         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2563         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2564     }
2566     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2570 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2572     NULL, /* child_added */
2573     NULL, /* child_removed */
2574     star_tb_event_attr_changed,
2575     NULL, /* content_changed */
2576     NULL  /* order_changed */
2577 };
2580 /**
2581  *  \param selection Should not be NULL.
2582  */
2583 static void
2584 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2586     int n_selected = 0;
2587     Inkscape::XML::Node *repr = NULL;
2589     purge_repr_listener( tbl, tbl );
2591     for (GSList const *items = selection->itemList();
2592          items != NULL;
2593          items = items->next)
2594     {
2595         if (SP_IS_STAR((SPItem *) items->data)) {
2596             n_selected++;
2597             repr = SP_OBJECT_REPR((SPItem *) items->data);
2598         }
2599     }
2601     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2603     if (n_selected == 0) {
2604         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2605     } else if (n_selected == 1) {
2606         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2608         if (repr) {
2609             g_object_set_data( tbl, "repr", repr );
2610             Inkscape::GC::anchor(repr);
2611             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2612             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2613         }
2614     } else {
2615         // FIXME: implement averaging of all parameters for multiple selected stars
2616         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2617         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2618     }
2622 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2624     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2625     // callbacks to lump all the changes for all selected objects in one undo step
2627     GtkAdjustment *adj = 0;
2629     // fixme: make settable in prefs!
2630     gint mag = 5;
2631     gdouble prop = 0.5;
2632     gboolean flat = FALSE;
2633     gdouble randomized = 0;
2634     gdouble rounded = 0;
2636     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2637     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2639     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2640     gtk_action_set_sensitive( sb2, !flat );
2642     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2643     gtk_adjustment_set_value(adj, mag);
2644     gtk_adjustment_value_changed(adj);
2646     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2647     gtk_adjustment_set_value(adj, prop);
2648     gtk_adjustment_value_changed(adj);
2650     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2651     gtk_adjustment_set_value(adj, rounded);
2652     gtk_adjustment_value_changed(adj);
2654     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2655     gtk_adjustment_set_value(adj, randomized);
2656     gtk_adjustment_value_changed(adj);
2660 void
2661 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2663     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2664     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2665     GtkWidget *l = gtk_label_new(NULL);
2666     gtk_label_set_markup(GTK_LABEL(l), title);
2667     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2668     if ( GTK_IS_TOOLBAR(tbl) ) {
2669         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2670     } else {
2671         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2672     }
2673     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2677 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2679     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
2681     {
2682         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2683         ege_output_action_set_use_markup( act, TRUE );
2684         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2685         g_object_set_data( holder, "mode_action", act );
2686     }
2688     {
2689         EgeAdjustmentAction* eact = 0;
2690         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2691         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2693         /* Flatsided checkbox */
2694         {
2695             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2697             GtkTreeIter iter;
2698             gtk_list_store_append( model, &iter );
2699             gtk_list_store_set( model, &iter,
2700                                 0, _("Polygon"),
2701                                 1, _("Regular polygon (with one handle) instead of a star"),
2702                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2703                                 -1 );
2705             gtk_list_store_append( model, &iter );
2706             gtk_list_store_set( model, &iter,
2707                                 0, _("Star"),
2708                                 1, _("Star instead of a regular polygon (with one handle)"),
2709                                 2, INKSCAPE_ICON_DRAW_STAR,
2710                                 -1 );
2712             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2713             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2714             g_object_set_data( holder, "flat_action", act );
2716             ege_select_one_action_set_appearance( act, "full" );
2717             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2718             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2719             ege_select_one_action_set_icon_column( act, 2 );
2720             ege_select_one_action_set_icon_size( act, secondarySize );
2721             ege_select_one_action_set_tooltip_column( act, 1  );
2723             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2724             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2725         }
2727         /* Magnitude */
2728         {
2729         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2730         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2731         eact = create_adjustment_action( "MagnitudeAction",
2732                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2733                                          "/tools/shapes/star/magnitude", 3,
2734                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2735                                          3, 1024, 1, 5,
2736                                          labels, values, G_N_ELEMENTS(labels),
2737                                          sp_stb_magnitude_value_changed,
2738                                          1.0, 0 );
2739         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2740         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2741         }
2743         /* Spoke ratio */
2744         {
2745         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2746         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2747         eact = create_adjustment_action( "SpokeAction",
2748                                          _("Spoke ratio"), _("Spoke ratio:"),
2749                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2750                                          // Base radius is the same for the closest handle.
2751                                          _("Base radius to tip radius ratio"),
2752                                          "/tools/shapes/star/proportion", 0.5,
2753                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2754                                          0.01, 1.0, 0.01, 0.1,
2755                                          labels, values, G_N_ELEMENTS(labels),
2756                                          sp_stb_proportion_value_changed );
2757         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2758         g_object_set_data( holder, "prop_action", eact );
2759         }
2761         if ( !isFlatSided ) {
2762             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2763         } else {
2764             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2765         }
2767         /* Roundedness */
2768         {
2769         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2770         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2771         eact = create_adjustment_action( "RoundednessAction",
2772                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2773                                          "/tools/shapes/star/rounded", 0.0,
2774                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2775                                          -10.0, 10.0, 0.01, 0.1,
2776                                          labels, values, G_N_ELEMENTS(labels),
2777                                          sp_stb_rounded_value_changed );
2778         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2779         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2780         }
2782         /* Randomization */
2783         {
2784         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2785         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2786         eact = create_adjustment_action( "RandomizationAction",
2787                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2788                                          "/tools/shapes/star/randomized", 0.0,
2789                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2790                                          -10.0, 10.0, 0.001, 0.01,
2791                                          labels, values, G_N_ELEMENTS(labels),
2792                                          sp_stb_randomized_value_changed, 0.1, 3 );
2793         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2794         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2795         }
2796     }
2798     {
2799         /* Reset */
2800         {
2801             GtkAction* act = gtk_action_new( "StarResetAction",
2802                                              _("Defaults"),
2803                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2804                                              GTK_STOCK_CLEAR );
2805             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2806             gtk_action_group_add_action( mainActions, act );
2807             gtk_action_set_sensitive( act, TRUE );
2808         }
2809     }
2811     sigc::connection *connection = new sigc::connection(
2812         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2813         );
2814     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2815     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2819 //########################
2820 //##       Rect         ##
2821 //########################
2823 static void sp_rtb_sensitivize( GObject *tbl )
2825     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2826     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2827     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2829     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2830         gtk_action_set_sensitive( not_rounded, FALSE );
2831     } else {
2832         gtk_action_set_sensitive( not_rounded, TRUE );
2833     }
2837 static void
2838 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2839                           void (*setter)(SPRect *, gdouble))
2841     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2843     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2844     SPUnit const *unit = tracker->getActiveUnit();
2846     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2847         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2848         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2849     }
2851     // quit if run by the attr_changed listener
2852     if (g_object_get_data( tbl, "freeze" )) {
2853         return;
2854     }
2856     // in turn, prevent listener from responding
2857     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2859     bool modmade = false;
2860     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2861     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2862         if (SP_IS_RECT(items->data)) {
2863             if (adj->value != 0) {
2864                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2865             } else {
2866                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2867             }
2868             modmade = true;
2869         }
2870     }
2872     sp_rtb_sensitivize( tbl );
2874     if (modmade) {
2875         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2876                                    _("Change rectangle"));
2877     }
2879     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2882 static void
2883 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2885     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2888 static void
2889 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2891     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2894 static void
2895 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2897     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2900 static void
2901 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2903     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2908 static void
2909 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2911     GtkAdjustment *adj = 0;
2913     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2914     gtk_adjustment_set_value(adj, 0.0);
2915     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2916     gtk_adjustment_value_changed(adj);
2918     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2919     gtk_adjustment_set_value(adj, 0.0);
2920     gtk_adjustment_value_changed(adj);
2922     sp_rtb_sensitivize( obj );
2925 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2926                                        gchar const */*old_value*/, gchar const */*new_value*/,
2927                                        bool /*is_interactive*/, gpointer data)
2929     GObject *tbl = G_OBJECT(data);
2931     // quit if run by the _changed callbacks
2932     if (g_object_get_data( tbl, "freeze" )) {
2933         return;
2934     }
2936     // in turn, prevent callbacks from responding
2937     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2939     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2940     SPUnit const *unit = tracker->getActiveUnit();
2942     gpointer item = g_object_get_data( tbl, "item" );
2943     if (item && SP_IS_RECT(item)) {
2944         {
2945             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2946             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2947             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2948         }
2950         {
2951             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2952             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2953             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2954         }
2956         {
2957             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2958             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2959             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2960         }
2962         {
2963             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2964             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2965             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2966         }
2967     }
2969     sp_rtb_sensitivize( tbl );
2971     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2975 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2976     NULL, /* child_added */
2977     NULL, /* child_removed */
2978     rect_tb_event_attr_changed,
2979     NULL, /* content_changed */
2980     NULL  /* order_changed */
2981 };
2983 /**
2984  *  \param selection should not be NULL.
2985  */
2986 static void
2987 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2989     int n_selected = 0;
2990     Inkscape::XML::Node *repr = NULL;
2991     SPItem *item = NULL;
2993     if ( g_object_get_data( tbl, "repr" ) ) {
2994         g_object_set_data( tbl, "item", NULL );
2995     }
2996     purge_repr_listener( tbl, tbl );
2998     for (GSList const *items = selection->itemList();
2999          items != NULL;
3000          items = items->next) {
3001         if (SP_IS_RECT((SPItem *) items->data)) {
3002             n_selected++;
3003             item = (SPItem *) items->data;
3004             repr = SP_OBJECT_REPR(item);
3005         }
3006     }
3008     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3010     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3012     if (n_selected == 0) {
3013         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3015         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3016         gtk_action_set_sensitive(w, FALSE);
3017         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3018         gtk_action_set_sensitive(h, FALSE);
3020     } else if (n_selected == 1) {
3021         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3022         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3024         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3025         gtk_action_set_sensitive(w, TRUE);
3026         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3027         gtk_action_set_sensitive(h, TRUE);
3029         if (repr) {
3030             g_object_set_data( tbl, "repr", repr );
3031             g_object_set_data( tbl, "item", item );
3032             Inkscape::GC::anchor(repr);
3033             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3034             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3035         }
3036     } else {
3037         // FIXME: implement averaging of all parameters for multiple selected
3038         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3039         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3040         sp_rtb_sensitivize( tbl );
3041     }
3045 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3047     EgeAdjustmentAction* eact = 0;
3048     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3050     {
3051         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3052         ege_output_action_set_use_markup( act, TRUE );
3053         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3054         g_object_set_data( holder, "mode_action", act );
3055     }
3057     // rx/ry units menu: create
3058     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3059     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3060     // fixme: add % meaning per cent of the width/height
3061     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3062     g_object_set_data( holder, "tracker", tracker );
3064     /* W */
3065     {
3066         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3067         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3068         eact = create_adjustment_action( "RectWidthAction",
3069                                          _("Width"), _("W:"), _("Width of rectangle"),
3070                                          "/tools/shapes/rect/width", 0,
3071                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3072                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3073                                          labels, values, G_N_ELEMENTS(labels),
3074                                          sp_rtb_width_value_changed );
3075         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3076         g_object_set_data( holder, "width_action", eact );
3077         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3078         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3079     }
3081     /* H */
3082     {
3083         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3084         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3085         eact = create_adjustment_action( "RectHeightAction",
3086                                          _("Height"), _("H:"), _("Height of rectangle"),
3087                                          "/tools/shapes/rect/height", 0,
3088                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3089                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3090                                          labels, values, G_N_ELEMENTS(labels),
3091                                          sp_rtb_height_value_changed );
3092         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3093         g_object_set_data( holder, "height_action", eact );
3094         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3095         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3096     }
3098     /* rx */
3099     {
3100         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3101         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3102         eact = create_adjustment_action( "RadiusXAction",
3103                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3104                                          "/tools/shapes/rect/rx", 0,
3105                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3106                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3107                                          labels, values, G_N_ELEMENTS(labels),
3108                                          sp_rtb_rx_value_changed);
3109         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3110         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3111     }
3113     /* ry */
3114     {
3115         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3116         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3117         eact = create_adjustment_action( "RadiusYAction",
3118                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3119                                          "/tools/shapes/rect/ry", 0,
3120                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3121                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3122                                          labels, values, G_N_ELEMENTS(labels),
3123                                          sp_rtb_ry_value_changed);
3124         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3125         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3126     }
3128     // add the units menu
3129     {
3130         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3131         gtk_action_group_add_action( mainActions, act );
3132     }
3134     /* Reset */
3135     {
3136         InkAction* inky = ink_action_new( "RectResetAction",
3137                                           _("Not rounded"),
3138                                           _("Make corners sharp"),
3139                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3140                                           secondarySize );
3141         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3142         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3143         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3144         g_object_set_data( holder, "not_rounded", inky );
3145     }
3147     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3148     sp_rtb_sensitivize( holder );
3150     sigc::connection *connection = new sigc::connection(
3151         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3152         );
3153     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3154     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3157 //########################
3158 //##       3D Box       ##
3159 //########################
3161 // normalize angle so that it lies in the interval [0,360]
3162 static double box3d_normalize_angle (double a) {
3163     double angle = a + ((int) (a/360.0))*360;
3164     if (angle < 0) {
3165         angle += 360.0;
3166     }
3167     return angle;
3170 static void
3171 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3172                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3173     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3174     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3175     //       are reset).
3176     bool is_infinite = !persp3d_VP_is_finite(persp->perspective_impl, axis);
3178     if (is_infinite) {
3179         gtk_toggle_action_set_active(tact, TRUE);
3180         gtk_action_set_sensitive(act, TRUE);
3182         double angle = persp3d_get_infinite_angle(persp, axis);
3183         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3184             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3185         }
3186     } else {
3187         gtk_toggle_action_set_active(tact, FALSE);
3188         gtk_action_set_sensitive(act, FALSE);
3189     }
3192 static void
3193 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3194     if (!persp_repr) {
3195         g_print ("No perspective given to box3d_resync_toolbar().\n");
3196         return;
3197     }
3199     GtkWidget *tbl = GTK_WIDGET(data);
3200     GtkAdjustment *adj = 0;
3201     GtkAction *act = 0;
3202     GtkToggleAction *tact = 0;
3203     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3204     if (!persp) {
3205         // Hmm, is it an error if this happens?
3206         return;
3207     }
3208     {
3209         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3210         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3211         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3213         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3214     }
3215     {
3216         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3217         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3218         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3220         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3221     }
3222     {
3223         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3224         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3225         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3227         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3228     }
3231 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3232                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3233                                                   bool /*is_interactive*/, gpointer data)
3235     GtkWidget *tbl = GTK_WIDGET(data);
3237     // quit if run by the attr_changed or selection changed listener
3238     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3239         return;
3240     }
3242     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3243     // sp_document_maybe_done() when the document is undo insensitive)
3244     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3246     // TODO: Only update the appropriate part of the toolbar
3247 //    if (!strcmp(name, "inkscape:vp_z")) {
3248         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3249 //    }
3251     Persp3D *persp = persp3d_get_from_repr(repr);
3252     persp3d_update_box_reprs(persp);
3254     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3257 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3259     NULL, /* child_added */
3260     NULL, /* child_removed */
3261     box3d_persp_tb_event_attr_changed,
3262     NULL, /* content_changed */
3263     NULL  /* order_changed */
3264 };
3266 /**
3267  *  \param selection Should not be NULL.
3268  */
3269 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3270 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3271 static void
3272 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3274     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3275     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3276     // update the perspectives with infinite VPs and leave the other ones untouched).
3278     Inkscape::XML::Node *persp_repr = NULL;
3279     purge_repr_listener(tbl, tbl);
3281     SPItem *item = selection->singleItem();
3282     if (item && SP_IS_BOX3D(item)) {
3283         // FIXME: Also deal with multiple selected boxes
3284         SPBox3D *box = SP_BOX3D(item);
3285         Persp3D *persp = box3d_get_perspective(box);
3286         persp_repr = SP_OBJECT_REPR(persp);
3287         if (persp_repr) {
3288             g_object_set_data(tbl, "repr", persp_repr);
3289             Inkscape::GC::anchor(persp_repr);
3290             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3291             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3292         }
3294         inkscape_active_document()->setCurrentPersp3D(persp3d_get_from_repr(persp_repr));
3295         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3296         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3298         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3299         box3d_resync_toolbar(persp_repr, tbl);
3300         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3301     }
3304 static void
3305 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3307     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3308     SPDocument *document = sp_desktop_document(desktop);
3310     // quit if run by the attr_changed or selection changed listener
3311     if (g_object_get_data( dataKludge, "freeze" )) {
3312         return;
3313     }
3315     // in turn, prevent listener from responding
3316     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3318     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3319     if (sel_persps.empty()) {
3320         // this can happen when the document is created; we silently ignore it
3321         return;
3322     }
3323     Persp3D *persp = sel_persps.front();
3325     persp->perspective_impl->tmat.set_infinite_direction (axis, adj->value);
3326     SP_OBJECT(persp)->updateRepr();
3328     // TODO: use the correct axis here, too
3329     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3331     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3335 static void
3336 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3338     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3341 static void
3342 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3344     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3347 static void
3348 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3350     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3354 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3356     // TODO: Take all selected perspectives into account
3357     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3358     if (sel_persps.empty()) {
3359         // this can happen when the document is created; we silently ignore it
3360         return;
3361     }
3362     Persp3D *persp = sel_persps.front();
3364     bool set_infinite = gtk_toggle_action_get_active(act);
3365     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3368 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3370     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3373 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3375     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3378 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3380     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3383 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3385     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3386     EgeAdjustmentAction* eact = 0;
3387     SPDocument *document = sp_desktop_document (desktop);
3388     Persp3DImpl *persp_impl = document->getCurrentPersp3DImpl();
3390     EgeAdjustmentAction* box3d_angle_x = 0;
3391     EgeAdjustmentAction* box3d_angle_y = 0;
3392     EgeAdjustmentAction* box3d_angle_z = 0;
3394     /* Angle X */
3395     {
3396         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3397         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3398         eact = create_adjustment_action( "3DBoxAngleXAction",
3399                                          _("Angle in X direction"), _("Angle X:"),
3400                                          // Translators: PL is short for 'perspective line'
3401                                          _("Angle of PLs in X direction"),
3402                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3403                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3404                                          -360.0, 360.0, 1.0, 10.0,
3405                                          labels, values, G_N_ELEMENTS(labels),
3406                                          box3d_angle_x_value_changed );
3407         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3408         g_object_set_data( holder, "box3d_angle_x_action", eact );
3409         box3d_angle_x = eact;
3410     }
3412     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::X)) {
3413         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3414     } else {
3415         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3416     }
3419     /* VP X state */
3420     {
3421         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3422                                                       // Translators: VP is short for 'vanishing point'
3423                                                       _("State of VP in X direction"),
3424                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3425                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3426                                                       Inkscape::ICON_SIZE_DECORATION );
3427         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3428         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3429         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3430         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3431         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3432     }
3434     /* Angle Y */
3435     {
3436         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3437         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3438         eact = create_adjustment_action( "3DBoxAngleYAction",
3439                                          _("Angle in Y direction"), _("Angle Y:"),
3440                                          // Translators: PL is short for 'perspective line'
3441                                          _("Angle of PLs in Y direction"),
3442                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3443                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3444                                          -360.0, 360.0, 1.0, 10.0,
3445                                          labels, values, G_N_ELEMENTS(labels),
3446                                          box3d_angle_y_value_changed );
3447         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3448         g_object_set_data( holder, "box3d_angle_y_action", eact );
3449         box3d_angle_y = eact;
3450     }
3452     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Y)) {
3453         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3454     } else {
3455         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3456     }
3458     /* VP Y state */
3459     {
3460         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3461                                                       // Translators: VP is short for 'vanishing point'
3462                                                       _("State of VP in Y direction"),
3463                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3464                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3465                                                       Inkscape::ICON_SIZE_DECORATION );
3466         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3467         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3468         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3469         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3470         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3471     }
3473     /* Angle Z */
3474     {
3475         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3476         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3477         eact = create_adjustment_action( "3DBoxAngleZAction",
3478                                          _("Angle in Z direction"), _("Angle Z:"),
3479                                          // Translators: PL is short for 'perspective line'
3480                                          _("Angle of PLs in Z direction"),
3481                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3482                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3483                                          -360.0, 360.0, 1.0, 10.0,
3484                                          labels, values, G_N_ELEMENTS(labels),
3485                                          box3d_angle_z_value_changed );
3486         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3487         g_object_set_data( holder, "box3d_angle_z_action", eact );
3488         box3d_angle_z = eact;
3489     }
3491     if (!persp_impl || !persp3d_VP_is_finite(persp_impl, Proj::Z)) {
3492         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3493     } else {
3494         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3495     }
3497     /* VP Z state */
3498     {
3499         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3500                                                       // Translators: VP is short for 'vanishing point'
3501                                                       _("State of VP in Z direction"),
3502                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3503                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3504                                                       Inkscape::ICON_SIZE_DECORATION );
3505         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3506         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3507         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3508         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3509         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3510     }
3512     sigc::connection *connection = new sigc::connection(
3513         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3514        );
3515     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3516     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3519 //########################
3520 //##       Spiral       ##
3521 //########################
3523 static void
3524 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3526     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3528     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3529         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3530         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3531     }
3533     // quit if run by the attr_changed listener
3534     if (g_object_get_data( tbl, "freeze" )) {
3535         return;
3536     }
3538     // in turn, prevent listener from responding
3539     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3541     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3543     bool modmade = false;
3544     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3545          items != NULL;
3546          items = items->next)
3547     {
3548         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3549             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3550             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3551             SP_OBJECT((SPItem *) items->data)->updateRepr();
3552             modmade = true;
3553         }
3554     }
3556     g_free(namespaced_name);
3558     if (modmade) {
3559         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3560                                    _("Change spiral"));
3561     }
3563     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3566 static void
3567 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3569     sp_spl_tb_value_changed(adj, tbl, "revolution");
3572 static void
3573 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3575     sp_spl_tb_value_changed(adj, tbl, "expansion");
3578 static void
3579 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3581     sp_spl_tb_value_changed(adj, tbl, "t0");
3584 static void
3585 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3587     GtkWidget *tbl = GTK_WIDGET(obj);
3589     GtkAdjustment *adj;
3591     // fixme: make settable
3592     gdouble rev = 5;
3593     gdouble exp = 1.0;
3594     gdouble t0 = 0.0;
3596     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3597     gtk_adjustment_set_value(adj, rev);
3598     gtk_adjustment_value_changed(adj);
3600     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3601     gtk_adjustment_set_value(adj, exp);
3602     gtk_adjustment_value_changed(adj);
3604     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3605     gtk_adjustment_set_value(adj, t0);
3606     gtk_adjustment_value_changed(adj);
3608     spinbutton_defocus(GTK_OBJECT(tbl));
3612 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3613                                          gchar const */*old_value*/, gchar const */*new_value*/,
3614                                          bool /*is_interactive*/, gpointer data)
3616     GtkWidget *tbl = GTK_WIDGET(data);
3618     // quit if run by the _changed callbacks
3619     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3620         return;
3621     }
3623     // in turn, prevent callbacks from responding
3624     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3626     GtkAdjustment *adj;
3627     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3628     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3630     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3631     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3633     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3634     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3636     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3640 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3641     NULL, /* child_added */
3642     NULL, /* child_removed */
3643     spiral_tb_event_attr_changed,
3644     NULL, /* content_changed */
3645     NULL  /* order_changed */
3646 };
3648 static void
3649 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3651     int n_selected = 0;
3652     Inkscape::XML::Node *repr = NULL;
3654     purge_repr_listener( tbl, tbl );
3656     for (GSList const *items = selection->itemList();
3657          items != NULL;
3658          items = items->next)
3659     {
3660         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3661             n_selected++;
3662             repr = SP_OBJECT_REPR((SPItem *) items->data);
3663         }
3664     }
3666     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3668     if (n_selected == 0) {
3669         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3670     } else if (n_selected == 1) {
3671         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3673         if (repr) {
3674             g_object_set_data( tbl, "repr", repr );
3675             Inkscape::GC::anchor(repr);
3676             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3677             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3678         }
3679     } else {
3680         // FIXME: implement averaging of all parameters for multiple selected
3681         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3682         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3683     }
3687 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3689     EgeAdjustmentAction* eact = 0;
3690     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3692     {
3693         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3694         ege_output_action_set_use_markup( act, TRUE );
3695         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3696         g_object_set_data( holder, "mode_action", act );
3697     }
3699     /* Revolution */
3700     {
3701         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3702         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3703         eact = create_adjustment_action( "SpiralRevolutionAction",
3704                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3705                                          "/tools/shapes/spiral/revolution", 3.0,
3706                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3707                                          0.01, 1024.0, 0.1, 1.0,
3708                                          labels, values, G_N_ELEMENTS(labels),
3709                                          sp_spl_tb_revolution_value_changed, 1, 2);
3710         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3711     }
3713     /* Expansion */
3714     {
3715         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3716         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3717         eact = create_adjustment_action( "SpiralExpansionAction",
3718                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3719                                          "/tools/shapes/spiral/expansion", 1.0,
3720                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3721                                          0.0, 1000.0, 0.01, 1.0,
3722                                          labels, values, G_N_ELEMENTS(labels),
3723                                          sp_spl_tb_expansion_value_changed);
3724         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3725     }
3727     /* T0 */
3728     {
3729         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3730         gdouble values[] = {0, 0.5, 0.9};
3731         eact = create_adjustment_action( "SpiralT0Action",
3732                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3733                                          "/tools/shapes/spiral/t0", 0.0,
3734                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3735                                          0.0, 0.999, 0.01, 1.0,
3736                                          labels, values, G_N_ELEMENTS(labels),
3737                                          sp_spl_tb_t0_value_changed);
3738         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3739     }
3741     /* Reset */
3742     {
3743         InkAction* inky = ink_action_new( "SpiralResetAction",
3744                                           _("Defaults"),
3745                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3746                                           GTK_STOCK_CLEAR,
3747                                           secondarySize );
3748         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3749         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3750     }
3753     sigc::connection *connection = new sigc::connection(
3754         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3755         );
3756     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3757     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3760 //########################
3761 //##     Pen/Pencil     ##
3762 //########################
3764 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3765 static Glib::ustring const
3766 freehand_tool_name(GObject *dataKludge)
3768     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3769     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3770              ? "/tools/freehand/pen"
3771              : "/tools/freehand/pencil" );
3774 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3776     gint mode = ege_select_one_action_get_active(act);
3778     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3779     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3781     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3783     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3784     // preparatory work here
3785     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3786         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3787         sp_pen_context_set_polyline_mode(pc);
3788     }
3791 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3793     /* Freehand mode toggle buttons */
3794     {
3795         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3796         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3797         Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
3799         {
3800             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3802             GtkTreeIter iter;
3803             gtk_list_store_append( model, &iter );
3804             gtk_list_store_set( model, &iter,
3805                                 0, _("Bezier"),
3806                                 1, _("Create regular Bezier path"),
3807                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3808                                 -1 );
3810             gtk_list_store_append( model, &iter );
3811             gtk_list_store_set( model, &iter,
3812                                 0, _("Spiro"),
3813                                 1, _("Create Spiro path"),
3814                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3815                                 -1 );
3817             if (!tool_is_pencil) {
3818                 gtk_list_store_append( model, &iter );
3819                 gtk_list_store_set( model, &iter,
3820                                     0, _("Zigzag"),
3821                                     1, _("Create a sequence of straight line segments"),
3822                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3823                                     -1 );
3825                 gtk_list_store_append( model, &iter );
3826                 gtk_list_store_set( model, &iter,
3827                                     0, _("Paraxial"),
3828                                     1, _("Create a sequence of paraxial line segments"),
3829                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3830                                     -1 );
3831             }
3833             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3834                                                                 "FreehandModeActionPencil" :
3835                                                                 "FreehandModeActionPen",
3836                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3837             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3839             ege_select_one_action_set_appearance( act, "full" );
3840             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3841             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3842             ege_select_one_action_set_icon_column( act, 2 );
3843             ege_select_one_action_set_icon_size( act, secondarySize );
3844             ege_select_one_action_set_tooltip_column( act, 1  );
3846             ege_select_one_action_set_active( act, freehandMode);
3847             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3848         }
3849     }
3852 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3853     gint shape = ege_select_one_action_get_active( act );
3854     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3855     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3858 /**
3859  * \brief Generate the list of freehand advanced shape option entries.
3860  */
3861 GList * freehand_shape_dropdown_items_list() {
3862     GList *glist = NULL;
3864     glist = g_list_append (glist, _("None"));
3865     glist = g_list_append (glist, _("Triangle in"));
3866     glist = g_list_append (glist, _("Triangle out"));
3867     glist = g_list_append (glist, _("Ellipse"));
3868     glist = g_list_append (glist, _("From clipboard"));
3870     return glist;
3873 static void
3874 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3875     /*advanced shape options */
3876     {
3877         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3878         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3880         GList* items = 0;
3881         gint count = 0;
3882         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3883         {
3884             GtkTreeIter iter;
3885             gtk_list_store_append( model, &iter );
3886             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3887             count++;
3888         }
3889         g_list_free( items );
3890         items = 0;
3891         EgeSelectOneAction* act1 = ege_select_one_action_new(
3892             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3893             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3894         g_object_set( act1, "short_label", _("Shape:"), NULL );
3895         ege_select_one_action_set_appearance( act1, "compact" );
3896         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3897         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3898         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3899         g_object_set_data( holder, "shape_action", act1 );
3900     }
3903 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3905     sp_add_freehand_mode_toggle(mainActions, holder, false);
3906     freehand_add_advanced_shape_options(mainActions, holder, false);
3910 static void
3911 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3913     GtkWidget *tbl = GTK_WIDGET(obj);
3915     GtkAdjustment *adj;
3917     // fixme: make settable
3918     gdouble tolerance = 4;
3920     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3921     gtk_adjustment_set_value(adj, tolerance);
3922     gtk_adjustment_value_changed(adj);
3924     spinbutton_defocus(GTK_OBJECT(tbl));
3927 static void
3928 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3930     // quit if run by the attr_changed listener
3931     if (g_object_get_data( tbl, "freeze" )) {
3932         return;
3933     }
3934     // in turn, prevent listener from responding
3935     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3936     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3937     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3938     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3941 /*
3942 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3943 public:
3944     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3945     {
3946         g_object_set_data(_obj, "prefobserver", this);
3947     }
3948     virtual ~PencilToleranceObserver() {
3949         if (g_object_get_data(_obj, "prefobserver") == this) {
3950             g_object_set_data(_obj, "prefobserver", NULL);
3951         }
3952     }
3953     virtual void notify(Inkscape::Preferences::Entry const &val) {
3954         GObject* tbl = _obj;
3955         if (g_object_get_data( tbl, "freeze" )) {
3956             return;
3957         }
3958         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3960         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3962         double v = val.getDouble(adj->value);
3963         gtk_adjustment_set_value(adj, v);
3964         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3965     }
3966 private:
3967     GObject *_obj;
3968 };
3969 */
3971 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3973     sp_add_freehand_mode_toggle(mainActions, holder, true);
3975     EgeAdjustmentAction* eact = 0;
3977     /* Tolerance */
3978     {
3979         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3980         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3981         eact = create_adjustment_action( "PencilToleranceAction",
3982                                          _("Smoothing:"), _("Smoothing: "),
3983                  _("How much smoothing (simplifying) is applied to the line"),
3984                                          "/tools/freehand/pencil/tolerance",
3985                                          3.0,
3986                                          GTK_WIDGET(desktop->canvas), NULL,
3987                                          holder, TRUE, "altx-pencil",
3988                                          1, 100.0, 0.5, 1.0,
3989                                          labels, values, G_N_ELEMENTS(labels),
3990                                          sp_pencil_tb_tolerance_value_changed,
3991                                          1, 2);
3992         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
3993         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3994     }
3996     /* advanced shape options */
3997     freehand_add_advanced_shape_options(mainActions, holder, true);
3999     /* Reset */
4000     {
4001         InkAction* inky = ink_action_new( "PencilResetAction",
4002                                           _("Defaults"),
4003                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4004                                           GTK_STOCK_CLEAR,
4005                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4006         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4007         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4008     }
4010     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4015 //########################
4016 //##       Tweak        ##
4017 //########################
4019 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4021     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4022     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4025 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4027     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4028     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4031 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4033     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4034     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4037 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4039     int mode = ege_select_one_action_get_active( act );
4040     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4041     prefs->setInt("/tools/tweak/mode", mode);
4043     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4044     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4045     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4046     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4047     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4048     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4049     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4050         if (doh) gtk_action_set_sensitive (doh, TRUE);
4051         if (dos) gtk_action_set_sensitive (dos, TRUE);
4052         if (dol) gtk_action_set_sensitive (dol, TRUE);
4053         if (doo) gtk_action_set_sensitive (doo, TRUE);
4054         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4055         if (fid) gtk_action_set_sensitive (fid, FALSE);
4056     } else {
4057         if (doh) gtk_action_set_sensitive (doh, FALSE);
4058         if (dos) gtk_action_set_sensitive (dos, FALSE);
4059         if (dol) gtk_action_set_sensitive (dol, FALSE);
4060         if (doo) gtk_action_set_sensitive (doo, FALSE);
4061         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4062         if (fid) gtk_action_set_sensitive (fid, TRUE);
4063     }
4066 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4068     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4069     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4072 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4073     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4074     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4076 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4077     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4078     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4080 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4081     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4082     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4084 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4085     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4086     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4089 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4091     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4092     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4094     {
4095         /* Width */
4096         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4097         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4098         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4099                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4100                                                               "/tools/tweak/width", 15,
4101                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4102                                                               1, 100, 1.0, 10.0,
4103                                                               labels, values, G_N_ELEMENTS(labels),
4104                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4105         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4106         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4107         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4108     }
4111     {
4112         /* Force */
4113         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4114         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4115         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4116                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4117                                                               "/tools/tweak/force", 20,
4118                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4119                                                               1, 100, 1.0, 10.0,
4120                                                               labels, values, G_N_ELEMENTS(labels),
4121                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4122         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4123         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4124         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4125     }
4127     /* Mode */
4128     {
4129         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4131         GtkTreeIter iter;
4132         gtk_list_store_append( model, &iter );
4133         gtk_list_store_set( model, &iter,
4134                             0, _("Move mode"),
4135                             1, _("Move objects in any direction"),
4136                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4137                             -1 );
4139         gtk_list_store_append( model, &iter );
4140         gtk_list_store_set( model, &iter,
4141                             0, _("Move in/out mode"),
4142                             1, _("Move objects towards cursor; with Shift from cursor"),
4143                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4144                             -1 );
4146         gtk_list_store_append( model, &iter );
4147         gtk_list_store_set( model, &iter,
4148                             0, _("Move jitter mode"),
4149                             1, _("Move objects in random directions"),
4150                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4151                             -1 );
4153         gtk_list_store_append( model, &iter );
4154         gtk_list_store_set( model, &iter,
4155                             0, _("Scale mode"),
4156                             1, _("Shrink objects, with Shift enlarge"),
4157                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4158                             -1 );
4160         gtk_list_store_append( model, &iter );
4161         gtk_list_store_set( model, &iter,
4162                             0, _("Rotate mode"),
4163                             1, _("Rotate objects, with Shift counterclockwise"),
4164                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4165                             -1 );
4167         gtk_list_store_append( model, &iter );
4168         gtk_list_store_set( model, &iter,
4169                             0, _("Duplicate/delete mode"),
4170                             1, _("Duplicate objects, with Shift delete"),
4171                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4172                             -1 );
4174         gtk_list_store_append( model, &iter );
4175         gtk_list_store_set( model, &iter,
4176                             0, _("Push mode"),
4177                             1, _("Push parts of paths in any direction"),
4178                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4179                             -1 );
4181         gtk_list_store_append( model, &iter );
4182         gtk_list_store_set( model, &iter,
4183                             0, _("Shrink/grow mode"),
4184                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4185                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4186                             -1 );
4188         gtk_list_store_append( model, &iter );
4189         gtk_list_store_set( model, &iter,
4190                             0, _("Attract/repel mode"),
4191                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4192                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4193                             -1 );
4195         gtk_list_store_append( model, &iter );
4196         gtk_list_store_set( model, &iter,
4197                             0, _("Roughen mode"),
4198                             1, _("Roughen parts of paths"),
4199                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4200                             -1 );
4202         gtk_list_store_append( model, &iter );
4203         gtk_list_store_set( model, &iter,
4204                             0, _("Color paint mode"),
4205                             1, _("Paint the tool's color upon selected objects"),
4206                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4207                             -1 );
4209         gtk_list_store_append( model, &iter );
4210         gtk_list_store_set( model, &iter,
4211                             0, _("Color jitter mode"),
4212                             1, _("Jitter the colors of selected objects"),
4213                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4214                             -1 );
4216         gtk_list_store_append( model, &iter );
4217         gtk_list_store_set( model, &iter,
4218                             0, _("Blur mode"),
4219                             1, _("Blur selected objects more; with Shift, blur less"),
4220                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4221                             -1 );
4224         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4225         g_object_set( act, "short_label", _("Mode:"), NULL );
4226         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4227         g_object_set_data( holder, "mode_action", act );
4229         ege_select_one_action_set_appearance( act, "full" );
4230         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4231         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4232         ege_select_one_action_set_icon_column( act, 2 );
4233         ege_select_one_action_set_icon_size( act, secondarySize );
4234         ege_select_one_action_set_tooltip_column( act, 1  );
4236         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4237         ege_select_one_action_set_active( act, mode );
4238         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4240         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4241     }
4243     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4245     {
4246         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4247         ege_output_action_set_use_markup( act, TRUE );
4248         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4249         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4250             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4251         g_object_set_data( holder, "tweak_channels_label", act);
4252     }
4254     {
4255         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4256                                                       _("Hue"),
4257                                                       _("In color mode, act on objects' hue"),
4258                                                       NULL,
4259                                                       Inkscape::ICON_SIZE_DECORATION );
4260         //TRANSLATORS:  "H" here stands for hue
4261         g_object_set( act, "short_label", _("H"), NULL );
4262         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4263         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4264         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4265         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4266             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4267         g_object_set_data( holder, "tweak_doh", act);
4268     }
4269     {
4270         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4271                                                       _("Saturation"),
4272                                                       _("In color mode, act on objects' saturation"),
4273                                                       NULL,
4274                                                       Inkscape::ICON_SIZE_DECORATION );
4275         //TRANSLATORS: "S" here stands for Saturation
4276         g_object_set( act, "short_label", _("S"), NULL );
4277         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4278         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4279         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4280         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4281             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4282         g_object_set_data( holder, "tweak_dos", act );
4283     }
4284     {
4285         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4286                                                       _("Lightness"),
4287                                                       _("In color mode, act on objects' lightness"),
4288                                                       NULL,
4289                                                       Inkscape::ICON_SIZE_DECORATION );
4290         //TRANSLATORS: "L" here stands for Lightness
4291         g_object_set( act, "short_label", _("L"), NULL );
4292         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4293         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4294         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4295         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4296             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4297         g_object_set_data( holder, "tweak_dol", act );
4298     }
4299     {
4300         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4301                                                       _("Opacity"),
4302                                                       _("In color mode, act on objects' opacity"),
4303                                                       NULL,
4304                                                       Inkscape::ICON_SIZE_DECORATION );
4305         //TRANSLATORS: "O" here stands for Opacity
4306         g_object_set( act, "short_label", _("O"), NULL );
4307         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4308         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4309         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4310         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4311             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4312         g_object_set_data( holder, "tweak_doo", act );
4313     }
4315     {   /* Fidelity */
4316         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4317         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4318         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4319                                                               _("Fidelity"), _("Fidelity:"),
4320                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4321                                                               "/tools/tweak/fidelity", 50,
4322                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4323                                                               1, 100, 1.0, 10.0,
4324                                                               labels, values, G_N_ELEMENTS(labels),
4325                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4326         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4327         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4328         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4329             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4330         g_object_set_data( holder, "tweak_fidelity", eact );
4331     }
4334     /* Use Pressure button */
4335     {
4336         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4337                                                       _("Pressure"),
4338                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4339                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4340                                                       Inkscape::ICON_SIZE_DECORATION );
4341         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4342         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4343         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4344     }
4349 //########################
4350 //##       Spray        ##
4351 //########################
4353 static void sp_spray_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4355     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4356     prefs->setDouble( "/tools/spray/width", adj->value );
4359 static void sp_spray_mean_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4361     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4362     prefs->setDouble( "/tools/spray/mean", adj->value );
4365 static void sp_spray_standard_deviation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4367     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4368     prefs->setDouble( "/tools/spray/standard_deviation", adj->value );
4371 static void sp_spray_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4373     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4374     prefs->setBool("/tools/spray/usepressure", gtk_toggle_action_get_active(act));
4377 static void sp_spray_mode_changed( EgeSelectOneAction *act, GObject */*tbl*/ )
4379     int mode = ege_select_one_action_get_active( act );
4380     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4381     prefs->setInt("/tools/spray/mode", mode);
4384 static void sp_spray_population_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4386     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4387     prefs->setDouble( "/tools/spray/population", adj->value );
4390 static void sp_spray_rotation_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4392     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4393     prefs->setDouble( "/tools/spray/rotation_variation", adj->value );
4396 static void sp_spray_scale_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4398     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4399     prefs->setDouble( "/tools/spray/scale_variation", adj->value );
4403 static void sp_spray_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4405     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
4406     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4408     {
4409         /* Width */
4410         gchar const* labels[] = {_("(narrow spray)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad spray)")};
4411         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4412         EgeAdjustmentAction *eact = create_adjustment_action( "SprayWidthAction",
4413                                                               _("Width"), _("Width:"), _("The width of the spray area (relative to the visible canvas area)"),
4414                                                               "/tools/spray/width", 15,
4415                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spray",
4416                                                               1, 100, 1.0, 10.0,
4417                                                               labels, values, G_N_ELEMENTS(labels),
4418                                                               sp_spray_width_value_changed,  1, 0 );
4419         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4420         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4421         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4422     }
4424     {
4425         /* Mean */
4426         gchar const* labels[] = {_("(minimum mean)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum mean)")};
4427         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4428         EgeAdjustmentAction *eact = create_adjustment_action( "SprayMeanAction",
4429                                                               _("Focus"), _("Focus:"), _("0 to spray a spot. Increase to enlarge the ring radius."),
4430                                                               "/tools/spray/mean", 0,
4431                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-mean",
4432                                                               0, 100, 1.0, 10.0,
4433                                                               labels, values, G_N_ELEMENTS(labels),
4434                                                               sp_spray_mean_value_changed,  1, 0 );
4435         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4436         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4437         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4438     }
4440     {
4441         /* Standard_deviation */
4442         gchar const* labels[] = {_("(minimum scatter)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum scatter)")};
4443         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4444         EgeAdjustmentAction *eact = create_adjustment_action( "SprayStandard_deviationAction",
4445                                                               _("Scatter"), _("Scatter:"), _("Increase to scatter sprayed objects."),
4446                                                               "/tools/spray/standard_deviation", 70,
4447                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-standard_deviation",
4448                                                               1, 100, 1.0, 10.0,
4449                                                               labels, values, G_N_ELEMENTS(labels),
4450                                                               sp_spray_standard_deviation_value_changed,  1, 0 );
4451         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4452         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4453         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4454     }
4456     /* Mode */
4457     {
4458         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4460         GtkTreeIter iter;
4461         gtk_list_store_append( model, &iter );
4462         gtk_list_store_set( model, &iter,
4463                             0, _("Spray with copies"),
4464                             1, _("Spray copies of the initial selection"),
4465                             2, INKSCAPE_ICON_SPRAY_COPY_MODE,
4466                             -1 );
4468         gtk_list_store_append( model, &iter );
4469         gtk_list_store_set( model, &iter,
4470                             0, _("Spray with clones"),
4471                             1, _("Spray clones of the initial selection"),
4472                             2, INKSCAPE_ICON_SPRAY_CLONE_MODE,
4473                             -1 );
4475         gtk_list_store_append( model, &iter );
4476         gtk_list_store_set( model, &iter,
4477                             0, _("Spray single path"),
4478                             1, _("Spray objects in a single path"),
4479                             2, INKSCAPE_ICON_SPRAY_UNION_MODE,
4480                             -1 );
4482         EgeSelectOneAction* act = ege_select_one_action_new( "SprayModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4483         g_object_set( act, "short_label", _("Mode:"), NULL );
4484         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4485         g_object_set_data( holder, "mode_action", act );
4487         ege_select_one_action_set_appearance( act, "full" );
4488         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4489         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4490         ege_select_one_action_set_icon_column( act, 2 );
4491         ege_select_one_action_set_icon_size( act, secondarySize );
4492         ege_select_one_action_set_tooltip_column( act, 1  );
4494         gint mode = prefs->getInt("/tools/spray/mode", 1);
4495         ege_select_one_action_set_active( act, mode );
4496         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_spray_mode_changed), holder );
4498         g_object_set_data( G_OBJECT(holder), "spray_tool_mode", act);
4499     }
4501     {   /* Population */
4502         gchar const* labels[] = {_("(low population)"), 0, 0, _("(default)"), 0, 0, _("(high population)")};
4503         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4504         EgeAdjustmentAction *eact = create_adjustment_action( "SprayPopulationAction",
4505                                                               _("Amount"), _("Amount:"),
4506                                                               _("Adjusts the number of items sprayed per clic."),
4507                                                               "/tools/spray/population", 70,
4508                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-population",
4509                                                               1, 100, 1.0, 10.0,
4510                                                               labels, values, G_N_ELEMENTS(labels),
4511                                                               sp_spray_population_value_changed,  1, 0 );
4512         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4513         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4514         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4515         g_object_set_data( holder, "spray_population", eact );
4516     }
4518     /* Use Pressure button */
4519     {
4520         InkToggleAction* act = ink_toggle_action_new( "SprayPressureAction",
4521                                                       _("Pressure"),
4522                                                       _("Use the pressure of the input device to alter the amount of sprayed objects."),
4523                                                       "use_pressure",
4524                                                       Inkscape::ICON_SIZE_DECORATION );
4525         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4526         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_spray_pressure_state_changed), NULL);
4527         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/spray/usepressure", true) );
4528     }
4530     {   /* Rotation */
4531         gchar const* labels[] = {_("(low rotation variation)"), 0, 0, _("(default)"), 0, 0, _("(high rotation variation)")};
4532         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4533         EgeAdjustmentAction *eact = create_adjustment_action( "SprayRotationAction",
4534                                                               _("Rotation"), _("Rotation:"),
4535                                                               // xgettext:no-c-format
4536                                                               _("Variation of the rotation of the sprayed objects. 0% for the same rotation than the original object."),
4537                                                               "/tools/spray/rotation_variation", 0,
4538                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-rotation",
4539                                                               0, 100, 1.0, 10.0,
4540                                                               labels, values, G_N_ELEMENTS(labels),
4541                                                               sp_spray_rotation_value_changed,  1, 0 );
4542         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4543         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4544         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4545         g_object_set_data( holder, "spray_rotation", eact );
4546     }
4548     {   /* Scale */
4549         gchar const* labels[] = {_("(low scale variation)"), 0, 0, _("(default)"), 0, 0, _("(high scale variation)")};
4550         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4551         EgeAdjustmentAction *eact = create_adjustment_action( "SprayScaleAction",
4552                                                               _("Scale"), _("Scale:"),
4553                                                               // xgettext:no-c-format
4554                                                               _("Variation in the scale of the sprayed objects. 0% for the same scale than the original object."),
4555                                                               "/tools/spray/scale_variation", 0,
4556                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "spray-scale",
4557                                                               0, 100, 1.0, 10.0,
4558                                                               labels, values, G_N_ELEMENTS(labels),
4559                                                               sp_spray_scale_value_changed,  1, 0 );
4560         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4561         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4562         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4563         g_object_set_data( holder, "spray_scale", eact );
4564     }
4571 //########################
4572 //##     Calligraphy    ##
4573 //########################
4574 static void update_presets_list (GObject *tbl)
4576     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4577     if (g_object_get_data(tbl, "presets_blocked"))
4578         return;
4580     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4581     if (!sel) {
4582         // WTF!? This will cause a segfault if ever reached
4583         //ege_select_one_action_set_active(sel, 0);
4584         return;
4585     }
4587     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4589     int ege_index = 1;
4590     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4591         bool match = true;
4593         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4594         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4595             Glib::ustring entry_name = j->getEntryName();
4596             if (entry_name == "id" || entry_name == "name") continue;
4598             void *widget = g_object_get_data(tbl, entry_name.data());
4599             if (widget) {
4600                 if (GTK_IS_ADJUSTMENT(widget)) {
4601                     double v = j->getDouble();
4602                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4603                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4604                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4605                         match = false;
4606                         break;
4607                     }
4608                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4609                     bool v = j->getBool();
4610                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4611                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4612                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4613                         match = false;
4614                         break;
4615                     }
4616                 }
4617             }
4618         }
4620         if (match) {
4621             // newly added item is at the same index as the
4622             // save command, so we need to change twice for it to take effect
4623             ege_select_one_action_set_active(sel, 0);
4624             ege_select_one_action_set_active(sel, ege_index); // one-based index
4625             return;
4626         }
4627     }
4629     // no match found
4630     ege_select_one_action_set_active(sel, 0);
4633 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4635     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4636     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4637     update_presets_list(tbl);
4640 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4642     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4643     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4644     update_presets_list(tbl);
4647 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4649     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4650     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4651     update_presets_list(tbl);
4654 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4656     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4657     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4658     update_presets_list(tbl);
4661 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4663     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4664     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4665     update_presets_list(tbl);
4668 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4670     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4671     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4672     update_presets_list(tbl);
4675 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4677     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4678     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4679     update_presets_list(tbl);
4682 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4684     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4685     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4686     update_presets_list(tbl);
4689 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4691     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4692     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4693     update_presets_list(tbl);
4696 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4698     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4699     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4700     update_presets_list(tbl);
4703 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4705     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4706     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4707     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4708     update_presets_list(tbl);
4709     if (calligraphy_angle )
4710         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4714 static gchar const *const widget_names[] = {
4715     "width",
4716     "mass",
4717     "wiggle",
4718     "angle",
4719     "thinning",
4720     "tremor",
4721     "flatness",
4722     "cap_rounding",
4723     "usepressure",
4724     "tracebackground",
4725     "usetilt"
4726 };
4729 static void sp_dcc_build_presets_list(GObject *tbl)
4731     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4733     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4734     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4735     gtk_list_store_clear (model);
4737     {
4738         GtkTreeIter iter;
4739         gtk_list_store_append( model, &iter );
4740         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4741     }
4743     // iterate over all presets to populate the list
4744     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4745     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4746     int ii=1;
4748     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4749         GtkTreeIter iter;
4750         Glib::ustring preset_name = prefs->getString(*i + "/name");
4751         gtk_list_store_append( model, &iter );
4752         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4753     }
4755     {
4756         GtkTreeIter iter;
4757         gtk_list_store_append( model, &iter );
4758         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4759         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4760     }
4762     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4764     update_presets_list (tbl);
4767 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4769     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4770     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4771     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4772     if (! desktop) return;
4774     if (g_object_get_data(tbl, "presets_blocked"))
4775         return;
4777     CalligraphicProfileRename::show(desktop);
4778     if ( !CalligraphicProfileRename::applied()) {
4779         // dialog cancelled
4780         update_presets_list (tbl);
4781         return;
4782     }
4783     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4785     if (profile_name.empty()) {
4786         // empty name entered
4787         update_presets_list (tbl);
4788         return;
4789     }
4791     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4793     // If there's a preset with the given name, find it and set save_path appropriately
4794     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4795     int total_presets = presets.size();
4796     int new_index = -1;
4797     Glib::ustring save_path; // profile pref path without a trailing slash
4799     int temp_index = 0;
4800     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4801         Glib::ustring name = prefs->getString(*i + "/name");
4802         if (!name.empty() && profile_name == name) {
4803             new_index = temp_index;
4804             save_path = *i;
4805             break;
4806         }
4807     }
4809     if (new_index == -1) {
4810         // no preset with this name, create
4811         new_index = total_presets + 1;
4812         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4813         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4814         g_free(profile_id);
4815     }
4817     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4818         gchar const *const widget_name = widget_names[i];
4819         void *widget = g_object_get_data(tbl, widget_name);
4820         if (widget) {
4821             if (GTK_IS_ADJUSTMENT(widget)) {
4822                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4823                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4824                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4825             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4826                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4827                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4828                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4829             } else {
4830                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4831             }
4832         } else {
4833             g_warning("Bad key when writing preset: %s\n", widget_name);
4834         }
4835     }
4836     prefs->setString(save_path + "/name", profile_name);
4838     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4839     sp_dcc_build_presets_list (tbl);
4843 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4845     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4847     gint preset_index = ege_select_one_action_get_active( act );
4848     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4849     // even when the preset is not changed. It would be good to replace it with something more
4850     // modern. Index 0 means "No preset", so we don't do anything.
4851     if (preset_index == 0) return;
4853     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4855     if (preset_index == save_presets_index) {
4856         // this is the Save command
4857         sp_dcc_save_profile(NULL, tbl);
4858         return;
4859     }
4861     if (g_object_get_data(tbl, "presets_blocked"))
4862         return;
4864     // preset_index is one-based so we subtract 1
4865     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4866     Glib::ustring preset_path = presets.at(preset_index - 1);
4868     if (!preset_path.empty()) {
4869         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
4871         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4873         // Shouldn't this be std::map?
4874         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4875             Glib::ustring entry_name = i->getEntryName();
4876             if (entry_name == "id" || entry_name == "name") continue;
4877             void *widget = g_object_get_data(tbl, entry_name.data());
4878             if (widget) {
4879                 if (GTK_IS_ADJUSTMENT(widget)) {
4880                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4881                     gtk_adjustment_set_value(adj, i->getDouble());
4882                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4883                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4884                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4885                     gtk_toggle_action_set_active(toggle, i->getBool());
4886                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4887                 } else {
4888                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4889                 }
4890             } else {
4891                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4892             }
4893         }
4894         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4895     }
4899 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4901     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4902     {
4903         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4905         EgeAdjustmentAction* calligraphy_angle = 0;
4907         {
4908         /* Width */
4909         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4910         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4911         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4912                                                               _("Pen Width"), _("Width:"),
4913                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4914                                                               "/tools/calligraphic/width", 15,
4915                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4916                                                               1, 100, 1.0, 10.0,
4917                                                               labels, values, G_N_ELEMENTS(labels),
4918                                                               sp_ddc_width_value_changed,  1, 0 );
4919         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4920         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4921         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4922         }
4924         {
4925         /* Thinning */
4926             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4927             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4928         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4929                                                               _("Stroke Thinning"), _("Thinning:"),
4930                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4931                                                               "/tools/calligraphic/thinning", 10,
4932                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4933                                                               -100, 100, 1, 10.0,
4934                                                               labels, values, G_N_ELEMENTS(labels),
4935                                                               sp_ddc_velthin_value_changed, 1, 0);
4936         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4937         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4938         }
4940         {
4941         /* Angle */
4942         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4943         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4944         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4945                                                               _("Pen Angle"), _("Angle:"),
4946                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4947                                                               "/tools/calligraphic/angle", 30,
4948                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4949                                                               -90.0, 90.0, 1.0, 10.0,
4950                                                               labels, values, G_N_ELEMENTS(labels),
4951                                                               sp_ddc_angle_value_changed, 1, 0 );
4952         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4953         g_object_set_data( holder, "angle_action", eact );
4954         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4955         calligraphy_angle = eact;
4956         }
4958         {
4959         /* Fixation */
4960             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4961         gdouble values[] = {0, 20, 40, 60, 90, 100};
4962         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4963                                                               _("Fixation"), _("Fixation:"),
4964                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4965                                                               "/tools/calligraphic/flatness", 90,
4966                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4967                                                               0.0, 100, 1.0, 10.0,
4968                                                               labels, values, G_N_ELEMENTS(labels),
4969                                                               sp_ddc_flatness_value_changed, 1, 0);
4970         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4971         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4972         }
4974         {
4975         /* Cap Rounding */
4976             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4977         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4978         // TRANSLATORS: "cap" means "end" (both start and finish) here
4979         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4980                                                               _("Cap rounding"), _("Caps:"),
4981                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4982                                                               "/tools/calligraphic/cap_rounding", 0.0,
4983                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4984                                                               0.0, 5.0, 0.01, 0.1,
4985                                                               labels, values, G_N_ELEMENTS(labels),
4986                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4987         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4988         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4989         }
4991         {
4992         /* Tremor */
4993             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4994         gdouble values[] = {0, 10, 20, 40, 60, 100};
4995         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4996                                                               _("Stroke Tremor"), _("Tremor:"),
4997                                                               _("Increase to make strokes rugged and trembling"),
4998                                                               "/tools/calligraphic/tremor", 0.0,
4999                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5000                                                               0.0, 100, 1, 10.0,
5001                                                               labels, values, G_N_ELEMENTS(labels),
5002                                                               sp_ddc_tremor_value_changed, 1, 0);
5004         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5005         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5006         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5007         }
5009         {
5010         /* Wiggle */
5011         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
5012         gdouble values[] = {0, 20, 40, 60, 100};
5013         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
5014                                                               _("Pen Wiggle"), _("Wiggle:"),
5015                                                               _("Increase to make the pen waver and wiggle"),
5016                                                               "/tools/calligraphic/wiggle", 0.0,
5017                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5018                                                               0.0, 100, 1, 10.0,
5019                                                               labels, values, G_N_ELEMENTS(labels),
5020                                                               sp_ddc_wiggle_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         /* Mass */
5028             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
5029         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
5030         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
5031                                                               _("Pen Mass"), _("Mass:"),
5032                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
5033                                                               "/tools/calligraphic/mass", 2.0,
5034                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
5035                                                               0.0, 100, 1, 10.0,
5036                                                               labels, values, G_N_ELEMENTS(labels),
5037                                                               sp_ddc_mass_value_changed, 1, 0);
5038         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5039         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5040         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5041         }
5044         /* Trace Background button */
5045         {
5046             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
5047                                                           _("Trace Background"),
5048                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
5049                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
5050                                                           Inkscape::ICON_SIZE_DECORATION );
5051             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5052             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
5053             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
5054             g_object_set_data( holder, "tracebackground", act );
5055         }
5057         /* Use Pressure button */
5058         {
5059             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
5060                                                           _("Pressure"),
5061                                                           _("Use the pressure of the input device to alter the width of the pen"),
5062                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
5063                                                           Inkscape::ICON_SIZE_DECORATION );
5064             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5065             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
5066             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
5067             g_object_set_data( holder, "usepressure", act );
5068         }
5070         /* Use Tilt button */
5071         {
5072             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
5073                                                           _("Tilt"),
5074                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
5075                                                           INKSCAPE_ICON_DRAW_USE_TILT,
5076                                                           Inkscape::ICON_SIZE_DECORATION );
5077             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5078             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
5079             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
5080             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
5081             g_object_set_data( holder, "usetilt", act );
5082         }
5084         /*calligraphic profile */
5085         {
5086             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5087             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
5088             ege_select_one_action_set_appearance (act1, "compact");
5089             g_object_set_data (holder, "profile_selector", act1 );
5091             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
5093             sp_dcc_build_presets_list (holder);
5095             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
5096             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
5097         }
5098     }
5102 //########################
5103 //##    Circle / Arc    ##
5104 //########################
5106 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
5108     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
5109     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
5111     if (v1 == 0 && v2 == 0) {
5112         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
5113             gtk_action_set_sensitive( ocb, FALSE );
5114             gtk_action_set_sensitive( make_whole, FALSE );
5115         }
5116     } else {
5117         gtk_action_set_sensitive( ocb, TRUE );
5118         gtk_action_set_sensitive( make_whole, TRUE );
5119     }
5122 static void
5123 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
5125     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5127     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5128         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5129         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
5130     }
5132     // quit if run by the attr_changed listener
5133     if (g_object_get_data( tbl, "freeze" )) {
5134         return;
5135     }
5137     // in turn, prevent listener from responding
5138     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5140     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
5142     bool modmade = false;
5143     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5144          items != NULL;
5145          items = items->next)
5146     {
5147         SPItem *item = SP_ITEM(items->data);
5149         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
5151             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
5152             SPArc *arc = SP_ARC(item);
5154             if (!strcmp(value_name, "start"))
5155                 ge->start = (adj->value * M_PI)/ 180;
5156             else
5157                 ge->end = (adj->value * M_PI)/ 180;
5159             sp_genericellipse_normalize(ge);
5160             ((SPObject *)arc)->updateRepr();
5161             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
5163             modmade = true;
5164         }
5165     }
5167     g_free(namespaced_name);
5169     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
5171     sp_arctb_sensitivize( tbl, adj->value, other->value );
5173     if (modmade) {
5174         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
5175                                    _("Arc: Change start/end"));
5176     }
5178     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5182 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
5184     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
5187 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
5189     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
5193 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
5195     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5196     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5197         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5198         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5199     }
5201     // quit if run by the attr_changed listener
5202     if (g_object_get_data( tbl, "freeze" )) {
5203         return;
5204     }
5206     // in turn, prevent listener from responding
5207     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5209     bool modmade = false;
5211     if ( ege_select_one_action_get_active(act) != 0 ) {
5212         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5213              items != NULL;
5214              items = items->next)
5215         {
5216             if (SP_IS_ARC((SPItem *) items->data)) {
5217                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5218                 repr->setAttribute("sodipodi:open", "true");
5219                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5220                 modmade = true;
5221             }
5222         }
5223     } else {
5224         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5225              items != NULL;
5226              items = items->next)
5227         {
5228             if (SP_IS_ARC((SPItem *) items->data))    {
5229                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5230                 repr->setAttribute("sodipodi:open", NULL);
5231                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5232                 modmade = true;
5233             }
5234         }
5235     }
5237     if (modmade) {
5238         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5239                                    _("Arc: Change open/closed"));
5240     }
5242     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5245 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5247     GtkAdjustment *adj;
5248     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5249     gtk_adjustment_set_value(adj, 0.0);
5250     gtk_adjustment_value_changed(adj);
5252     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5253     gtk_adjustment_set_value(adj, 0.0);
5254     gtk_adjustment_value_changed(adj);
5256     spinbutton_defocus( GTK_OBJECT(obj) );
5259 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5260                                       gchar const */*old_value*/, gchar const */*new_value*/,
5261                                       bool /*is_interactive*/, gpointer data)
5263     GObject *tbl = G_OBJECT(data);
5265     // quit if run by the _changed callbacks
5266     if (g_object_get_data( tbl, "freeze" )) {
5267         return;
5268     }
5270     // in turn, prevent callbacks from responding
5271     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5273     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5274     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5276     GtkAdjustment *adj1,*adj2;
5277     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5278     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5279     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5280     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5282     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5284     char const *openstr = NULL;
5285     openstr = repr->attribute("sodipodi:open");
5286     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5288     if (openstr) {
5289         ege_select_one_action_set_active( ocb, 1 );
5290     } else {
5291         ege_select_one_action_set_active( ocb, 0 );
5292     }
5294     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5297 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5298     NULL, /* child_added */
5299     NULL, /* child_removed */
5300     arc_tb_event_attr_changed,
5301     NULL, /* content_changed */
5302     NULL  /* order_changed */
5303 };
5306 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5308     int n_selected = 0;
5309     Inkscape::XML::Node *repr = NULL;
5311     purge_repr_listener( tbl, tbl );
5313     for (GSList const *items = selection->itemList();
5314          items != NULL;
5315          items = items->next)
5316     {
5317         if (SP_IS_ARC((SPItem *) items->data)) {
5318             n_selected++;
5319             repr = SP_OBJECT_REPR((SPItem *) items->data);
5320         }
5321     }
5323     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5325     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5326     if (n_selected == 0) {
5327         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5328     } else if (n_selected == 1) {
5329         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5330         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5332         if (repr) {
5333             g_object_set_data( tbl, "repr", repr );
5334             Inkscape::GC::anchor(repr);
5335             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5336             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5337         }
5338     } else {
5339         // FIXME: implement averaging of all parameters for multiple selected
5340         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5341         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5342         sp_arctb_sensitivize( tbl, 1, 0 );
5343     }
5347 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5349     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5351     EgeAdjustmentAction* eact = 0;
5352     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
5355     {
5356         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5357         ege_output_action_set_use_markup( act, TRUE );
5358         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5359         g_object_set_data( holder, "mode_action", act );
5360     }
5362     /* Start */
5363     {
5364         eact = create_adjustment_action( "ArcStartAction",
5365                                          _("Start"), _("Start:"),
5366                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5367                                          "/tools/shapes/arc/start", 0.0,
5368                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5369                                          -360.0, 360.0, 1.0, 10.0,
5370                                          0, 0, 0,
5371                                          sp_arctb_start_value_changed);
5372         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5373     }
5375     /* End */
5376     {
5377         eact = create_adjustment_action( "ArcEndAction",
5378                                          _("End"), _("End:"),
5379                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5380                                          "/tools/shapes/arc/end", 0.0,
5381                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5382                                          -360.0, 360.0, 1.0, 10.0,
5383                                          0, 0, 0,
5384                                          sp_arctb_end_value_changed);
5385         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5386     }
5388     /* Segments / Pie checkbox */
5389     {
5390         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5392         GtkTreeIter iter;
5393         gtk_list_store_append( model, &iter );
5394         gtk_list_store_set( model, &iter,
5395                             0, _("Closed arc"),
5396                             1, _("Switch to segment (closed shape with two radii)"),
5397                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5398                             -1 );
5400         gtk_list_store_append( model, &iter );
5401         gtk_list_store_set( model, &iter,
5402                             0, _("Open Arc"),
5403                             1, _("Switch to arc (unclosed shape)"),
5404                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5405                             -1 );
5407         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5408         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5409         g_object_set_data( holder, "open_action", act );
5411         ege_select_one_action_set_appearance( act, "full" );
5412         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5413         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5414         ege_select_one_action_set_icon_column( act, 2 );
5415         ege_select_one_action_set_icon_size( act, secondarySize );
5416         ege_select_one_action_set_tooltip_column( act, 1  );
5418         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5419         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5420         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5421     }
5423     /* Make Whole */
5424     {
5425         InkAction* inky = ink_action_new( "ArcResetAction",
5426                                           _("Make whole"),
5427                                           _("Make the shape a whole ellipse, not arc or segment"),
5428                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5429                                           secondarySize );
5430         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5431         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5432         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5433         g_object_set_data( holder, "make_whole", inky );
5434     }
5436     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5437     // sensitivize make whole and open checkbox
5438     {
5439         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5440         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5441         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5442     }
5445     sigc::connection *connection = new sigc::connection(
5446         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5447         );
5448     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5449     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5455 // toggle button callbacks and updaters
5457 //########################
5458 //##      Dropper       ##
5459 //########################
5461 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5462     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5463     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5464     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5465     if ( set_action ) {
5466         if ( gtk_toggle_action_get_active( act ) ) {
5467             gtk_action_set_sensitive( set_action, TRUE );
5468         } else {
5469             gtk_action_set_sensitive( set_action, FALSE );
5470         }
5471     }
5473     spinbutton_defocus(GTK_OBJECT(tbl));
5476 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5477     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5478     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5479     spinbutton_defocus(GTK_OBJECT(tbl));
5483 /**
5484  * Dropper auxiliary toolbar construction and setup.
5485  *
5486  * TODO: Would like to add swatch of current color.
5487  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5488  *       can drag and drop places. Will provide a nice mixing palette.
5489  */
5490 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5492     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5493     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5495     {
5496         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5497         ege_output_action_set_use_markup( act, TRUE );
5498         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5499     }
5501     {
5502         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5503                                                       _("Pick opacity"),
5504                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5505                                                       NULL,
5506                                                       Inkscape::ICON_SIZE_DECORATION );
5507         g_object_set( act, "short_label", _("Pick"), NULL );
5508         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5509         g_object_set_data( holder, "pick_action", act );
5510         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5511         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5512     }
5514     {
5515         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5516                                                       _("Assign opacity"),
5517                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5518                                                       NULL,
5519                                                       Inkscape::ICON_SIZE_DECORATION );
5520         g_object_set( act, "short_label", _("Assign"), NULL );
5521         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5522         g_object_set_data( holder, "set_action", act );
5523         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5524         // make sure it's disabled if we're not picking alpha
5525         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5526         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5527     }
5531 //########################
5532 //##      LPETool       ##
5533 //########################
5535 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5537 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5538 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5540     using namespace Inkscape::LivePathEffect;
5542     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5543     SPEventContext *ec = desktop->event_context;
5544     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5545         return;
5546     }
5548     // only take action if run by the attr_changed listener
5549     if (!g_object_get_data(tbl, "freeze")) {
5550         // in turn, prevent listener from responding
5551         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5553         gint mode = ege_select_one_action_get_active(act);
5554         EffectType type = lpesubtools[mode].type;
5556         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5557         bool success = lpetool_try_construction(lc, type);
5558         if (success) {
5559             // since the construction was already performed, we set the state back to inactive
5560             ege_select_one_action_set_active(act, 0);
5561             mode = 0;
5562         } else {
5563             // switch to the chosen subtool
5564             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5565         }
5567         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5568             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5569             prefs->setInt( "/tools/lpetool/mode", mode );
5570         }
5572         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5573     }
5576 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5578     SPEventContext *ec = selection->desktop()->event_context;
5579     if (!SP_IS_LPETOOL_CONTEXT(ec))
5580         return;
5582     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5585 void
5586 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5588     using namespace Inkscape::LivePathEffect;
5589     SPEventContext *ec = selection->desktop()->event_context;
5590     if (!SP_IS_LPETOOL_CONTEXT(ec))
5591         return;
5592     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5594     lpetool_delete_measuring_items(lc);
5595     lpetool_create_measuring_items(lc, selection);
5597     // activate line segment combo box if a single item with LPELineSegment is selected
5598     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5599     SPItem *item = selection->singleItem();
5600     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5601         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5602         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5603         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5604             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5605             g_object_set_data(tbl, "currentlpe", lpe);
5606             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5607             gtk_action_set_sensitive(w, TRUE);
5608             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5609         } else {
5610             g_object_set_data(tbl, "currentlpe", NULL);
5611             g_object_set_data(tbl, "currentlpeitem", NULL);
5612             gtk_action_set_sensitive(w, FALSE);
5613         }
5614     } else {
5615         g_object_set_data(tbl, "currentlpe", NULL);
5616         g_object_set_data(tbl, "currentlpeitem", NULL);
5617         gtk_action_set_sensitive(w, FALSE);
5618     }
5621 static void
5622 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5623     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5624     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5626     bool show = gtk_toggle_action_get_active( act );
5627     prefs->setBool("/tools/lpetool/show_bbox",  show);
5629     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5630         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5631         lpetool_context_reset_limiting_bbox(lc);
5632     }
5635 static void
5636 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5637     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5638     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5639         return;
5641     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5642     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5643     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5644         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5645         bool show = gtk_toggle_action_get_active( act );
5646         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5647         lpetool_show_measuring_info(lc, show);
5648         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5649     }
5652 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5653     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5654     SPUnit const *unit = tracker->getActiveUnit();
5655     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5656     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5658     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5659     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5660         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5661         lpetool_delete_measuring_items(lc);
5662         lpetool_create_measuring_items(lc);
5663     }
5666 static void
5667 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5668     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5669     Inkscape::Selection *selection = desktop->selection;
5671     Geom::OptRect bbox = selection->bounds();
5673     if (bbox) {
5674         Geom::Point A(bbox->min());
5675         Geom::Point B(bbox->max());
5677         A *= desktop->doc2dt();
5678         B *= desktop->doc2dt();
5680         // TODO: should we provide a way to store points in prefs?
5681         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5682         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5683         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5684         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5685         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5687         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5688     }
5690     gtk_toggle_action_set_active(act, false);
5693 static void
5694 sp_line_segment_build_list(GObject *tbl)
5696     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5698     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5699     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5700     gtk_list_store_clear (model);
5702     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5703     {
5704         GtkTreeIter iter;
5705         gtk_list_store_append( model, &iter );
5706         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5707         gtk_list_store_append( model, &iter );
5708         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5709         gtk_list_store_append( model, &iter );
5710         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5711         gtk_list_store_append( model, &iter );
5712         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5713     }
5715     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5718 static void
5719 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5720     using namespace Inkscape::LivePathEffect;
5722     // quit if run by the attr_changed listener
5723     if (g_object_get_data(tbl, "freeze")) {
5724         return;
5725     }
5727     // in turn, prevent listener from responding
5728     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5730     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5731     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5732     if (lpeitem) {
5733         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5734         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5735         sp_lpe_item_update_patheffect(lpeitem, true, true);
5736     }
5738     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5741 static void
5742 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5743     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5745     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5746         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5747     }
5748     gtk_toggle_action_set_active(act, false);
5751 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5753     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5754     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5755     g_object_set_data(holder, "tracker", tracker);
5756     SPUnit const *unit = tracker->getActiveUnit();
5758     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5759     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5761     /** Automatically create a list of LPEs that get added to the toolbar **/
5762     {
5763         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5765         GtkTreeIter iter;
5767         // the first toggle button represents the state that no subtool is active (remove this when
5768         // this can be modeled by EgeSelectOneAction or some other action)
5769         gtk_list_store_append( model, &iter );
5770         gtk_list_store_set( model, &iter,
5771                             0, _("All inactive"),
5772                             1, _("No geometric tool is active"),
5773                             2, "draw-geometry-inactive",
5774                             -1 );
5776         Inkscape::LivePathEffect::EffectType type;
5777         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5778             type =  lpesubtools[i].type;
5779             gtk_list_store_append( model, &iter );
5780             gtk_list_store_set( model, &iter,
5781                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5782                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5783                                 2, lpesubtools[i].icon_name,
5784                                 -1 );
5785         }
5787         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5788         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5789         g_object_set_data( holder, "lpetool_mode_action", act );
5791         ege_select_one_action_set_appearance( act, "full" );
5792         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5793         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5794         ege_select_one_action_set_icon_column( act, 2 );
5795         ege_select_one_action_set_tooltip_column( act, 1  );
5797         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5798         ege_select_one_action_set_active( act, lpeToolMode );
5799         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5800     }
5802     /* Show limiting bounding box */
5803     {
5804         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5805                                                       _("Show limiting bounding box"),
5806                                                       _("Show bounding box (used to cut infinite lines)"),
5807                                                       "show-bounding-box",
5808                                                       Inkscape::ICON_SIZE_DECORATION );
5809         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5810         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5811         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5812     }
5814     /* Set limiting bounding box to bbox of current selection */
5815     {
5816         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5817                                                       _("Get limiting bounding box from selection"),
5818                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5819                                                       "draw-geometry-set-bounding-box",
5820                                                       Inkscape::ICON_SIZE_DECORATION );
5821         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5822         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5823         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5824     }
5827     /* Combo box to choose line segment type */
5828     {
5829         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5830         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5831         ege_select_one_action_set_appearance (act, "compact");
5832         g_object_set_data (holder, "lpetool_line_segment_action", act );
5834         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5836         sp_line_segment_build_list (holder);
5838         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5839         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5840         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5841     }
5843     /* Display measuring info for selected items */
5844     {
5845         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5846                                                       _("Display measuring info"),
5847                                                       _("Display measuring info for selected items"),
5848                                                       "draw-geometry-show-measuring-info",
5849                                                       Inkscape::ICON_SIZE_DECORATION );
5850         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5851         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5852         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5853     }
5855     // add the units menu
5856     {
5857         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5858         gtk_action_group_add_action( mainActions, act );
5859         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5860         g_object_set_data(holder, "lpetool_units_action", act);
5861         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5862     }
5864     /* Open LPE dialog (to adapt parameters numerically) */
5865     {
5866         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5867                                                       _("Open LPE dialog"),
5868                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5869                                                       "dialog-geometry",
5870                                                       Inkscape::ICON_SIZE_DECORATION );
5871         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5872         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5873         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5874     }
5876     //watch selection
5877     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5879     sigc::connection *c_selection_modified =
5880         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5881                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5882     pool->add_connection ("selection-modified", c_selection_modified);
5884     sigc::connection *c_selection_changed =
5885         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5886                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5887     pool->add_connection ("selection-changed", c_selection_changed);
5890 //########################
5891 //##       Eraser       ##
5892 //########################
5894 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5896     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5897     prefs->setDouble( "/tools/eraser/width", adj->value );
5898     update_presets_list(tbl);
5901 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5903     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5904     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5905     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5906         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5907         prefs->setBool( "/tools/eraser/mode", eraserMode );
5908     }
5910     // only take action if run by the attr_changed listener
5911     if (!g_object_get_data( tbl, "freeze" )) {
5912         // in turn, prevent listener from responding
5913         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5915         if ( eraserMode != 0 ) {
5916         } else {
5917         }
5918         // TODO finish implementation
5920         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5921     }
5924 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5926     {
5927         /* Width */
5928         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5929         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5930         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5931                                                               _("Pen Width"), _("Width:"),
5932                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5933                                                               "/tools/eraser/width", 15,
5934                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5935                                                               1, 100, 1.0, 10.0,
5936                                                               labels, values, G_N_ELEMENTS(labels),
5937                                                               sp_erc_width_value_changed, 1, 0);
5938         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5939         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5940         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5941     }
5943     {
5944         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5946         GtkTreeIter iter;
5947         gtk_list_store_append( model, &iter );
5948         gtk_list_store_set( model, &iter,
5949                             0, _("Delete"),
5950                             1, _("Delete objects touched by the eraser"),
5951                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5952                             -1 );
5954         gtk_list_store_append( model, &iter );
5955         gtk_list_store_set( model, &iter,
5956                             0, _("Cut"),
5957                             1, _("Cut out from objects"),
5958                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5959                             -1 );
5961         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5962         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5963         g_object_set_data( holder, "eraser_mode_action", act );
5965         ege_select_one_action_set_appearance( act, "full" );
5966         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5967         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5968         ege_select_one_action_set_icon_column( act, 2 );
5969         ege_select_one_action_set_tooltip_column( act, 1  );
5971         /// @todo Convert to boolean?
5972         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5973         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5974         ege_select_one_action_set_active( act, eraserMode );
5975         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5976     }
5980 //########################
5981 //##    Text Toolbox    ##
5982 //########################
5983 /*
5984 static void
5985 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5987     //Call back for letter sizing spinbutton
5990 static void
5991 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5993     //Call back for line height spinbutton
5996 static void
5997 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5999     //Call back for horizontal kerning spinbutton
6002 static void
6003 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
6005     //Call back for vertical kerning spinbutton
6008 static void
6009 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
6011     //Call back for letter rotation spinbutton
6012 }*/
6014 namespace {
6016 void
6017 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
6019     // quit if run by the _changed callbacks
6020     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6021         return;
6022     }
6024     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6026     SPStyle *query =
6027         sp_style_new (SP_ACTIVE_DOCUMENT);
6029     int result_family =
6030         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6032     int result_style =
6033         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6035     int result_numbers =
6036         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6038     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6040     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6041     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
6042         // there are no texts in selection, read from prefs
6044         sp_style_read_from_prefs(query, "/tools/text");
6046         if (g_object_get_data(tbl, "text_style_from_prefs")) {
6047             // do not reset the toolbar style from prefs if we already did it last time
6048             sp_style_unref(query);
6049             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6050             return;
6051         }
6052         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
6053     } else {
6054         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
6055     }
6057     if (query->text)
6058     {
6059         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
6060             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6061             gtk_entry_set_text (GTK_ENTRY (entry), "");
6063         } else if (query->text->font_specification.value || query->text->font_family.value) {
6065             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
6066             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6068             // Get the font that corresponds
6069             Glib::ustring familyName;
6071             font_instance * font = font_factory::Default()->FaceFromStyle(query);
6072             if (font) {
6073                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
6074                 font->Unref();
6075                 font = NULL;
6076             }
6078             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
6080             Gtk::TreeIter iter;
6081             try {
6082                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
6083                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
6084                 iter = model->get_iter(path);
6085             } catch (...) {
6086                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
6087                 sp_style_unref(query);
6088                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6089                 return;
6090             }
6092             combo->set_active (iter);
6093         }
6095         //Size
6096         {
6097             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
6098             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
6099             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
6100             g_free(str);
6101         }
6103         //Anchor
6104         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
6105         {
6106             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
6107             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6108             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6109             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6110         }
6111         else
6112         {
6113             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
6114             {
6115                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
6116                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6117                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6118                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6119             }
6120             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
6121             {
6122                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
6123                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6124                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6125                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6126             }
6127             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
6128             {
6129                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
6130                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6131                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6132                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6133             }
6134         }
6136         //Style
6137         {
6138             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
6140             gboolean active = gtk_toggle_button_get_active (button);
6141             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));
6143             if (active != check)
6144             {
6145                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6146                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6147                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6148             }
6149         }
6151         {
6152             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
6154             gboolean active = gtk_toggle_button_get_active (button);
6155             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
6157             if (active != check)
6158             {
6159                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6160                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
6161                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6162             }
6163         }
6165         //Orientation
6166         //locking both buttons, changing one affect all group (both)
6167         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
6168         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6170         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
6171         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
6173         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
6174         {
6175             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
6176         }
6177         else
6178         {
6179             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
6180         }
6181         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6182         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
6183     }
6185     sp_style_unref(query);
6187     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6190 void
6191 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
6193     sp_text_toolbox_selection_changed (selection, tbl);
6196 void
6197 sp_text_toolbox_subselection_changed (gpointer /*tc*/, GObject *tbl)
6199     sp_text_toolbox_selection_changed (NULL, tbl);
6202 void
6203 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6204                                 GObject             *tbl)
6206     // quit if run by the _changed callbacks
6207     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6208         return;
6209     }
6211     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6213     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6214     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6215     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6217     //g_print ("family changed to: %s\n", family);
6219     SPStyle *query =
6220         sp_style_new (SP_ACTIVE_DOCUMENT);
6222     int result_fontspec =
6223         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6225     SPCSSAttr *css = sp_repr_css_attr_new ();
6227     // First try to get the font spec from the stored value
6228     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6230     if (fontSpec.empty()) {
6231         // Construct a new font specification if it does not yet exist
6232         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6233         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6234         fontFromStyle->Unref();
6235     }
6237     if (!fontSpec.empty()) {
6239         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6241         if (!newFontSpec.empty()) {
6243             if (fontSpec != newFontSpec) {
6245                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6247                 if (font) {
6248                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6250                     // Set all the these just in case they were altered when finding the best
6251                     // match for the new family and old style...
6253                     gchar c[256];
6255                     font->Family(c, 256);
6257                     sp_repr_css_set_property (css, "font-family", c);
6259                     font->Attribute( "weight", c, 256);
6260                     sp_repr_css_set_property (css, "font-weight", c);
6262                     font->Attribute("style", c, 256);
6263                     sp_repr_css_set_property (css, "font-style", c);
6265                     font->Attribute("stretch", c, 256);
6266                     sp_repr_css_set_property (css, "font-stretch", c);
6268                     font->Attribute("variant", c, 256);
6269                     sp_repr_css_set_property (css, "font-variant", c);
6271                     font->Unref();
6272                 }
6273             }
6275         } else {
6276             // If the old font on selection (or default) was not existing on the system,
6277             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6278             // setting the family reported by the family chooser.
6280             //g_print ("fallback setting family: %s\n", family);
6281             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6282             sp_repr_css_set_property (css, "font-family", family);
6283         }
6284     }
6286     // If querying returned nothing, set the default style of the tool (for new texts)
6287     if (result_fontspec == QUERY_STYLE_NOTHING)
6288     {
6289         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6290         prefs->mergeStyle("/tools/text/style", css);
6291         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6292     }
6293     else
6294     {
6295         sp_desktop_set_style (desktop, css, true, true);
6296     }
6298     sp_style_unref(query);
6300     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6301                                    _("Text: Change font family"));
6302     sp_repr_css_attr_unref (css);
6304     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6306     // unfreeze
6307     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6309     // focus to canvas
6310     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6314 void
6315 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6316                                    gpointer          data)
6318     if (g_object_get_data (G_OBJECT (button), "block")) return;
6319     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6320     int prop = GPOINTER_TO_INT(data);
6322     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6324     // move the x of all texts to preserve the same bbox
6325     Inkscape::Selection *selection = sp_desktop_selection(desktop);
6326     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
6327         if (SP_IS_TEXT((SPItem *) items->data)) {
6328             SPItem *item = SP_ITEM(items->data);
6330             unsigned writing_mode = SP_OBJECT_STYLE(item)->writing_mode.value;
6331             // below, variable names suggest horizontal move, but we check the writing direction
6332             // and move in the corresponding axis
6333             int axis;
6334             if (writing_mode == SP_CSS_WRITING_MODE_LR_TB || writing_mode == SP_CSS_WRITING_MODE_RL_TB) {
6335                 axis = NR::X;
6336             } else {
6337                 axis = NR::Y;
6338             }
6340             Geom::OptRect bbox
6341                   = item->getBounds(Geom::identity(), SPItem::GEOMETRIC_BBOX);
6342             if (!bbox)
6343                 continue;
6344             double width = bbox->dimensions()[axis];
6345             // If you want to align within some frame, other than the text's own bbox, calculate
6346             // the left and right (or top and bottom for tb text) slacks of the text inside that
6347             // frame (currently unused)
6348             double left_slack = 0;
6349             double right_slack = 0;
6350             unsigned old_align = SP_OBJECT_STYLE(item)->text_align.value;
6351             double move = 0;
6352             if (old_align == SP_CSS_TEXT_ALIGN_START || old_align == SP_CSS_TEXT_ALIGN_LEFT) {
6353                 switch (prop) {
6354                     case 0:
6355                         move = -left_slack;
6356                         break;
6357                     case 1:
6358                         move = width/2 + (right_slack - left_slack)/2;
6359                         break;
6360                     case 2:
6361                         move = width + right_slack;
6362                         break;
6363                 }
6364             } else if (old_align == SP_CSS_TEXT_ALIGN_CENTER) {
6365                 switch (prop) {
6366                     case 0:
6367                         move = -width/2 - left_slack;
6368                         break;
6369                     case 1:
6370                         move = (right_slack - left_slack)/2;
6371                         break;
6372                     case 2:
6373                         move = width/2 + right_slack;
6374                         break;
6375                 }
6376             } else if (old_align == SP_CSS_TEXT_ALIGN_END || old_align == SP_CSS_TEXT_ALIGN_RIGHT) {
6377                 switch (prop) {
6378                     case 0:
6379                         move = -width - left_slack;
6380                         break;
6381                     case 1:
6382                         move = -width/2 + (right_slack - left_slack)/2;
6383                         break;
6384                     case 2:
6385                         move = right_slack;
6386                         break;
6387                 }
6388             }
6389             Geom::Point XY = SP_TEXT(item)->attributes.firstXY();
6390             if (axis == NR::X) {
6391                 XY = XY + Geom::Point (move, 0);
6392             } else {
6393                 XY = XY + Geom::Point (0, move);
6394             }
6395             SP_TEXT(item)->attributes.setFirstXY(XY);
6396             SP_OBJECT(item)->updateRepr();
6397             SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
6398         }
6399     }
6401     SPCSSAttr *css = sp_repr_css_attr_new ();
6402     switch (prop)
6403     {
6404         case 0:
6405         {
6406             sp_repr_css_set_property (css, "text-anchor", "start");
6407             sp_repr_css_set_property (css, "text-align", "start");
6408             break;
6409         }
6410         case 1:
6411         {
6412             sp_repr_css_set_property (css, "text-anchor", "middle");
6413             sp_repr_css_set_property (css, "text-align", "center");
6414             break;
6415         }
6417         case 2:
6418         {
6419             sp_repr_css_set_property (css, "text-anchor", "end");
6420             sp_repr_css_set_property (css, "text-align", "end");
6421             break;
6422         }
6424         case 3:
6425         {
6426             sp_repr_css_set_property (css, "text-anchor", "start");
6427             sp_repr_css_set_property (css, "text-align", "justify");
6428             break;
6429         }
6430     }
6432     SPStyle *query =
6433         sp_style_new (SP_ACTIVE_DOCUMENT);
6434     int result_numbers =
6435         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6437     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6438     if (result_numbers == QUERY_STYLE_NOTHING)
6439     {
6440         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6441         prefs->mergeStyle("/tools/text/style", css);
6442     }
6444     sp_style_unref(query);
6446     sp_desktop_set_style (desktop, css, true, true);
6447     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6448                                    _("Text: Change alignment"));
6449     sp_repr_css_attr_unref (css);
6451     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6454 void
6455 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6456                                gpointer          data)
6458     if (g_object_get_data (G_OBJECT (button), "block")) return;
6460     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6461     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6462     int          prop       = GPOINTER_TO_INT(data);
6463     bool         active     = gtk_toggle_button_get_active (button);
6465     SPStyle *query =
6466         sp_style_new (SP_ACTIVE_DOCUMENT);
6468     int result_fontspec =
6469         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6471     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6472     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6473     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6475     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6476     Glib::ustring newFontSpec = "";
6478     if (fontSpec.empty()) {
6479         // Construct a new font specification if it does not yet exist
6480         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6481         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6482         fontFromStyle->Unref();
6483     }
6485     bool nochange = true;
6486     switch (prop)
6487     {
6488         case 0:
6489         {
6490             if (!fontSpec.empty()) {
6491                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6492                 if (!newFontSpec.empty()) {
6493                     // Don't even set the bold if the font didn't exist on the system
6494                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6495                     nochange = false;
6496                 }
6497             }
6498             // set or reset the button according
6499             if(nochange) {
6500                 gboolean check = gtk_toggle_button_get_active (button);
6502                 if (active != check)
6503                 {
6504                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6505                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6506                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6507                 }
6508             }
6510             break;
6511         }
6513         case 1:
6514         {
6515             if (!fontSpec.empty()) {
6516                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6517                 if (!newFontSpec.empty()) {
6518                     // Don't even set the italic if the font didn't exist on the system
6519                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6520                     nochange = false;
6521                 }
6522             }
6523             if(nochange) {
6524                 gboolean check = gtk_toggle_button_get_active (button);
6526                 if (active != check)
6527                 {
6528                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6529                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6530                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6531                 }
6532             }
6533             break;
6534         }
6535     }
6537     if (!newFontSpec.empty()) {
6538         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6539     }
6541     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6542     if (result_fontspec == QUERY_STYLE_NOTHING)
6543     {
6544         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6545         prefs->mergeStyle("/tools/text/style", css);
6546     }
6548     sp_style_unref(query);
6550     sp_desktop_set_style (desktop, css, true, true);
6551     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6552                                    _("Text: Change font style"));
6553     sp_repr_css_attr_unref (css);
6555     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6558 void
6559 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6560                                      gpointer         data)
6562     if (g_object_get_data (G_OBJECT (button), "block")) {
6563         return;
6564     }
6566     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6567     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6568     int          prop       = GPOINTER_TO_INT(data);
6570     switch (prop)
6571     {
6572         case 0:
6573         {
6574             sp_repr_css_set_property (css, "writing-mode", "lr");
6575             break;
6576         }
6578         case 1:
6579         {
6580             sp_repr_css_set_property (css, "writing-mode", "tb");
6581             break;
6582         }
6583     }
6585     SPStyle *query =
6586         sp_style_new (SP_ACTIVE_DOCUMENT);
6587     int result_numbers =
6588         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6590     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6591     if (result_numbers == QUERY_STYLE_NOTHING)
6592     {
6593         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6594         prefs->mergeStyle("/tools/text/style", css);
6595     }
6597     sp_desktop_set_style (desktop, css, true, true);
6598     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6599                                    _("Text: Change orientation"));
6600     sp_repr_css_attr_unref (css);
6602     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6605 gboolean
6606 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6608     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6609     if (!desktop) return FALSE;
6611     switch (get_group0_keyval (event)) {
6612         case GDK_KP_Enter: // chosen
6613         case GDK_Return:
6614             // unfreeze and update, which will defocus
6615             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6616             sp_text_toolbox_family_changed (NULL, tbl);
6617             return TRUE; // I consumed the event
6618             break;
6619         case GDK_Escape:
6620             // defocus
6621             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6622             return TRUE; // I consumed the event
6623             break;
6624     }
6625     return FALSE;
6628 gboolean
6629 sp_text_toolbox_family_list_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject */*tbl*/)
6631     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6632     if (!desktop) return FALSE;
6634     switch (get_group0_keyval (event)) {
6635         case GDK_KP_Enter:
6636         case GDK_Return:
6637         case GDK_Escape: // defocus
6638             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6639             return TRUE; // I consumed the event
6640             break;
6641         case GDK_w:
6642         case GDK_W:
6643             if (event->state & GDK_CONTROL_MASK) {
6644                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6645                 return TRUE; // I consumed the event
6646             }
6647             break;
6648     }
6649     return FALSE;
6653 void
6654 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6655                                GObject     *tbl)
6657      // quit if run by the _changed callbacks
6658     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6659         return;
6660     }
6662     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6664    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6666     // If this is not from selecting a size in the list (in which case get_active will give the
6667     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6668     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6669     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6670    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6671         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6672         return;
6673    }
6675     gdouble value = -1;
6676     {
6677         gchar *endptr;
6678         gchar *const text = gtk_combo_box_get_active_text(cbox);
6679         if (text) {
6680             value = g_strtod(text, &endptr);
6681             if (endptr == text) {  // Conversion failed, non-numeric input.
6682                 value = -1;
6683             }
6684             g_free(text);
6685         }
6686     }
6687     if (value <= 0) {
6688         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6689         return; // could not parse value
6690     }
6692     SPCSSAttr *css = sp_repr_css_attr_new ();
6693     Inkscape::CSSOStringStream osfs;
6694     osfs << value;
6695     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6697     SPStyle *query =
6698         sp_style_new (SP_ACTIVE_DOCUMENT);
6699     int result_numbers =
6700         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6702     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6703     if (result_numbers == QUERY_STYLE_NOTHING)
6704     {
6705         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6706         prefs->mergeStyle("/tools/text/style", css);
6707     }
6709     sp_style_unref(query);
6711     sp_desktop_set_style (desktop, css, true, true);
6712     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6713                                    _("Text: Change font size"));
6714     sp_repr_css_attr_unref (css);
6716     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6718     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6721 gboolean
6722 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6724     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6725     if (!desktop) return FALSE;
6727     if (!g_object_get_data (tbl, "esc-pressed")) {
6728         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6729         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6730         sp_text_toolbox_size_changed (cbox, tbl);
6731         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6732     }
6733     return FALSE; // I consumed the event
6737 gboolean
6738 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6740     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6741     if (!desktop) return FALSE;
6743     switch (get_group0_keyval (event)) {
6744         case GDK_Escape: // defocus
6745             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6746             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6747             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6748             return TRUE; // I consumed the event
6749             break;
6750         case GDK_Return: // defocus
6751         case GDK_KP_Enter:
6752             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6753             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6754             sp_text_toolbox_size_changed (cbox, tbl);
6755             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6756             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6757             return TRUE; // I consumed the event
6758             break;
6759     }
6760     return FALSE;
6763 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6764 // does not work!
6765 gboolean
6766 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6767                                  GdkEventFocus    */*event*/,
6768                                  GObject          *tbl)
6770     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6771     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6772     return FALSE;
6775 gboolean
6776 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6777                                  GdkEventFocus    */*event*/,
6778                                  GObject          *tbl)
6780     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6781     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6782     return FALSE;
6785 void
6786 cell_data_func  (GtkCellLayout */*cell_layout*/,
6787                  GtkCellRenderer   *cell,
6788                  GtkTreeModel      *tree_model,
6789                  GtkTreeIter       *iter,
6790                  gpointer           /*data*/)
6792     gchar *family;
6793     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6794     gchar *const family_escaped = g_markup_escape_text(family, -1);
6796     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6797     int show_sample = prefs->getInt("/tools/text/show_sample_in_list", 1);
6798     if (show_sample) {
6800         Glib::ustring sample = prefs->getString("/tools/text/font_sample");
6801         gchar *const sample_escaped = g_markup_escape_text(sample.data(), -1);
6803     std::stringstream markup;
6804     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6805            << family_escaped << "'>" << sample_escaped << "</span>";
6806     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6808         g_free(sample_escaped);
6809     } else {
6810         g_object_set (G_OBJECT (cell), "markup", family_escaped, NULL);
6811     }
6813     g_free(family);
6814     g_free(family_escaped);
6817 gboolean text_toolbox_completion_match_selected(GtkEntryCompletion */*widget*/,
6818                                                 GtkTreeModel       *model,
6819                                                 GtkTreeIter        *iter,
6820                                                 GObject            *tbl)
6822     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6823     // press Enter again after choosing a completion)
6824     gchar *family = 0;
6825     gtk_tree_model_get(model, iter, 0, &family, -1);
6827     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6828     gtk_entry_set_text (GTK_ENTRY (entry), family);
6830     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6831     sp_text_toolbox_family_changed (NULL, tbl);
6832     return TRUE;
6836 static void
6837 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6838     GtkEntry *entry;
6839     GtkEntryCompletion *completion;
6840     GtkTreeModel *model;
6842     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6843     completion = gtk_entry_completion_new();
6844     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6845     gtk_entry_completion_set_model(completion, model);
6846     gtk_entry_completion_set_text_column(completion, 0);
6847     gtk_entry_completion_set_inline_completion(completion, FALSE);
6848     gtk_entry_completion_set_inline_selection(completion, FALSE);
6849     gtk_entry_completion_set_popup_completion(completion, TRUE);
6850     gtk_entry_set_completion(entry, completion);
6852     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6854     g_object_unref(completion);
6857 void sp_text_toolbox_family_popnotify(GtkComboBox *widget,
6858                                       void */*property*/,
6859                                       GObject *tbl)
6861   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6863   gboolean shown;
6864   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6865   if (shown) {
6866          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6867          //g_print("POP: notify: SHOWN\n");
6868   } else {
6869          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6871          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6872          // find out if the drop down was closed by Enter and if so, manually update (only
6873          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6874          GdkEvent *ev = gtk_get_current_event();
6875          if (ev) {
6876              //g_print ("ev type: %d\n", ev->type);
6877              if (ev->type == GDK_KEY_PRESS) {
6878                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6879                      case GDK_KP_Enter: // chosen
6880                      case GDK_Return:
6881                      {
6882                          // make sure the chosen one is inserted into the entry
6883                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6884                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6885                          GtkTreeIter iter;
6886                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6887                          if (has_active) {
6888                              gchar *family;
6889                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6890                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6891                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6892                          }
6894                          // update
6895                          sp_text_toolbox_family_changed (NULL, tbl);
6896                          break;
6897                      }
6898                  }
6899              }
6900          }
6902          // regardless of whether we updated, defocus the widget
6903          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6904          if (desktop)
6905              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6906          //g_print("POP: notify: HIDDEN\n");
6907   }
6910 GtkWidget *sp_text_toolbox_new (SPDesktop *desktop)
6912     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6913     GtkIconSize secondarySize = static_cast<GtkIconSize>(ToolboxFactory::prefToSize("/toolbox/secondary", 1));
6915     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6916     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6918     GtkTooltips *tt = gtk_tooltips_new();
6920     ////////////Family
6921     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6922     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6924     gtk_rc_parse_string (
6925        "style \"dropdown-as-list-style\"\n"
6926        "{\n"
6927        "    GtkComboBox::appears-as-list = 1\n"
6928        "}\n"
6929        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6930     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6931     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6933     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6935     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6937     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6938     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6940     // expand the field a bit so as to view more of the previews in the drop-down
6941     GtkRequisition req;
6942     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6943     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), MIN(req.width + 50, 500), -1);
6945     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6946     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6948     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6949     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6950              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6951     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6952     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6953     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6955     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6956     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6958     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6959     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6960     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6961     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6963     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6964     GtkWidget *box = gtk_event_box_new ();
6965     gtk_container_add (GTK_CONTAINER (box), image);
6966     gtk_toolbar_append_widget( tbl, box, "", "");
6967     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6968     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6969     gtk_widget_hide (GTK_WIDGET (box));
6970     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6972     ////////////Size
6973     gchar const *const sizes[] = {
6974         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6975         "16", "18", "20", "22", "24", "28",
6976         "32", "36", "40", "48", "56", "64", "72", "144"
6977     };
6979     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6980     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6981         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6982     }
6983     gtk_widget_set_size_request (cbox, 80, -1);
6984     gtk_toolbar_append_widget( tbl, cbox, "", "");
6985     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6986     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6987     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6988     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6990     ////////////Text anchor
6991     GtkWidget *group   = gtk_radio_button_new (NULL);
6992     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6993     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6995     // left
6996     GtkWidget *rbutton = group;
6997     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6998     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6999     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7001     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7002     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
7003     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
7004     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
7006     // center
7007     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7008     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7009     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
7010     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7012     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7013     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
7014     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
7015     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
7017     // right
7018     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7019     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7020     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
7021     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7023     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7024     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
7025     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
7026     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
7028     // fill
7029     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7030     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7031     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
7032     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7034     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7035     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
7036     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
7037     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
7039     gtk_toolbar_append_widget( tbl, row, "", "");
7041     //spacer
7042     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7044     ////////////Text style
7045     row = gtk_hbox_new (FALSE, 4);
7047     // bold
7048     rbutton = gtk_toggle_button_new ();
7049     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7050     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
7051     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7052     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
7054     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7055     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
7056     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
7058     // italic
7059     rbutton = gtk_toggle_button_new ();
7060     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7061     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
7062     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7063     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
7065     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7066     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
7067     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
7069     gtk_toolbar_append_widget( tbl, row, "", "");
7071     //spacer
7072     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
7074     // Text orientation
7075     group   = gtk_radio_button_new (NULL);
7076     row     = gtk_hbox_new (FALSE, 4);
7077     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
7079     // horizontal
7080     rbutton = group;
7081     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7082     gtk_container_add           (GTK_CONTAINER (rbutton),
7083                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
7084     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7085     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
7087     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7088     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
7089     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
7091     // vertical
7092     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
7093     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
7094     gtk_container_add           (GTK_CONTAINER (rbutton),
7095                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
7096     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
7097     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
7099     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
7100     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
7101     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
7102     gtk_toolbar_append_widget( tbl, row, "", "" );
7105     //watch selection
7106     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
7108     sigc::connection *c_selection_changed =
7109         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
7110                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
7111     pool->add_connection ("selection-changed", c_selection_changed);
7113     sigc::connection *c_selection_modified =
7114         new sigc::connection (sp_desktop_selection (desktop)->connectModified
7115                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
7116     pool->add_connection ("selection-modified", c_selection_modified);
7118     sigc::connection *c_subselection_changed =
7119         new sigc::connection (desktop->connectToolSubselectionChanged
7120                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
7121     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
7123     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
7126     gtk_widget_show_all( GTK_WIDGET(tbl) );
7128     return GTK_WIDGET(tbl);
7129 } // end of sp_text_toolbox_new()
7131 }//<unnamed> namespace
7134 //#########################
7135 //##      Connector      ##
7136 //#########################
7138 static void sp_connector_mode_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7140     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7141     prefs->setBool("/tools/connector/mode",
7142                 gtk_toggle_action_get_active( act ));
7145 static void sp_connector_path_set_avoid(void)
7147     cc_selection_set_avoid(true);
7151 static void sp_connector_path_set_ignore(void)
7153     cc_selection_set_avoid(false);
7156 static void sp_connector_orthogonal_toggled( GtkToggleAction* act, GObject *tbl )
7158     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7159     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7160     SPDocument *doc = sp_desktop_document(desktop);
7162     if (!sp_document_get_undo_sensitive(doc))
7163     {
7164         return;
7165     }
7168     // quit if run by the _changed callbacks
7169     if (g_object_get_data( tbl, "freeze" )) {
7170         return;
7171     }
7173     // in turn, prevent callbacks from responding
7174     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7176     bool is_orthog = gtk_toggle_action_get_active( act );
7177     gchar orthog_str[] = "orthogonal";
7178     gchar polyline_str[] = "polyline";
7179     gchar *value = is_orthog ? orthog_str : polyline_str ;
7181     bool modmade = false;
7182     GSList *l = (GSList *) selection->itemList();
7183     while (l) {
7184         SPItem *item = (SPItem *) l->data;
7186         if (cc_item_is_connector(item)) {
7187             sp_object_setAttribute(item, "inkscape:connector-type",
7188                     value, false);
7189             item->avoidRef->handleSettingChange();
7190             modmade = true;
7191         }
7192         l = l->next;
7193     }
7195     if (!modmade)
7196     {
7197         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7198         prefs->setBool("/tools/connector/orthogonal", is_orthog);
7199     }
7201     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7202             is_orthog ? _("Set connector type: orthogonal"): _("Set connector type: polyline"));
7204     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7207 static void connector_curvature_changed(GtkAdjustment *adj, GObject* tbl)
7209     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7210     Inkscape::Selection * selection = sp_desktop_selection(desktop);
7211     SPDocument *doc = sp_desktop_document(desktop);
7213     if (!sp_document_get_undo_sensitive(doc))
7214     {
7215         return;
7216     }
7219     // quit if run by the _changed callbacks
7220     if (g_object_get_data( tbl, "freeze" )) {
7221         return;
7222     }
7224     // in turn, prevent callbacks from responding
7225     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
7227     gdouble newValue = gtk_adjustment_get_value(adj);
7228     gchar value[G_ASCII_DTOSTR_BUF_SIZE];
7229     g_ascii_dtostr(value, G_ASCII_DTOSTR_BUF_SIZE, newValue);
7231     bool modmade = false;
7232     GSList *l = (GSList *) selection->itemList();
7233     while (l) {
7234         SPItem *item = (SPItem *) l->data;
7236         if (cc_item_is_connector(item)) {
7237             sp_object_setAttribute(item, "inkscape:connector-curvature",
7238                     value, false);
7239             item->avoidRef->handleSettingChange();
7240             modmade = true;
7241         }
7242         l = l->next;
7243     }
7245     if (!modmade)
7246     {
7247         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7248         prefs->setDouble(Glib::ustring("/tools/connector/curvature"), newValue);
7249     }
7251     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7252             _("Change connector curvature"));
7254     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7258 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
7260     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7261     SPDocument *doc = sp_desktop_document(desktop);
7263     if (!sp_document_get_undo_sensitive(doc))
7264     {
7265         return;
7266     }
7268     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7270     if ( !repr->attribute("inkscape:connector-spacing") &&
7271             ( adj->value == defaultConnSpacing )) {
7272         // Don't need to update the repr if the attribute doesn't
7273         // exist and it is being set to the default value -- as will
7274         // happen at startup.
7275         return;
7276     }
7278     // quit if run by the attr_changed listener
7279     if (g_object_get_data( tbl, "freeze" )) {
7280         return;
7281     }
7283     // in turn, prevent listener from responding
7284     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
7286     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
7287     SP_OBJECT(desktop->namedview)->updateRepr();
7289     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
7290     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
7291         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
7292         Geom::Matrix m = Geom::identity();
7293         avoid_item_move(&m, item);
7294     }
7296     if (items) {
7297         g_slist_free(items);
7298     }
7300     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
7301             _("Change connector spacing"));
7303     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
7306 static void sp_connector_graph_layout(void)
7308     if (!SP_ACTIVE_DESKTOP) return;
7309     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7311     // hack for clones, see comment in align-and-distribute.cpp
7312     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7313     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
7315     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
7317     prefs->setInt("/options/clonecompensation/value", saved_compensation);
7319     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
7322 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7324     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7325     prefs->setBool("/tools/connector/directedlayout",
7326                 gtk_toggle_action_get_active( act ));
7329 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
7331     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7332     prefs->setBool("/tools/connector/avoidoverlaplayout",
7333                 gtk_toggle_action_get_active( act ));
7337 static void connector_length_changed(GtkAdjustment *adj, GObject* /*tbl*/)
7339     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7340     prefs->setDouble("/tools/connector/length", adj->value);
7343 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
7344                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
7345                                             bool /*is_interactive*/, gpointer data)
7347     GtkWidget *tbl = GTK_WIDGET(data);
7349     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
7350         return;
7351     }
7352     if (strcmp(name, "inkscape:connector-spacing") == 0)
7353     {
7354         GtkAdjustment *adj = (GtkAdjustment*)
7355                 gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
7356         gdouble spacing = defaultConnSpacing;
7357         sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
7359         gtk_adjustment_set_value(adj, spacing);
7360         gtk_adjustment_value_changed(adj);
7361     }
7363     spinbutton_defocus(GTK_OBJECT(tbl));
7366 static void sp_connector_new_connection_point(GtkWidget *, GObject *tbl)
7368     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7369     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7371     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7372         cc_create_connection_point(cc);
7375 static void sp_connector_remove_connection_point(GtkWidget *, GObject *tbl)
7377     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
7378     SPConnectorContext* cc = SP_CONNECTOR_CONTEXT(desktop->event_context);
7380     if (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE)
7381         cc_remove_connection_point(cc);
7384 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
7385     NULL, /* child_added */
7386     NULL, /* child_removed */
7387     connector_tb_event_attr_changed,
7388     NULL, /* content_changed */
7389     NULL  /* order_changed */
7390 };
7392 static void sp_connector_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
7394     GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "curvature" ) );
7395     GtkToggleAction *act = GTK_TOGGLE_ACTION( g_object_get_data( tbl, "orthogonal" ) );
7396     SPItem *item = selection->singleItem();
7397     if (SP_IS_PATH(item))
7398     {
7399         gdouble curvature = SP_PATH(item)->connEndPair.getCurvature();
7400         bool is_orthog = SP_PATH(item)->connEndPair.isOrthogonal();
7401         gtk_toggle_action_set_active(act, is_orthog);
7402         gtk_adjustment_set_value(adj, curvature);
7403     }
7407 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
7409     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7410     Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
7412     // Editing mode toggle button
7413     {
7414         InkToggleAction* act = ink_toggle_action_new( "ConnectorEditModeAction",
7415                                                       _("EditMode"),
7416                                                       _("Switch between connection point editing and connector drawing mode"),
7417                                                       INKSCAPE_ICON_CONNECTOR_EDIT,
7418                                                       Inkscape::ICON_SIZE_DECORATION );
7419         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7421         bool tbuttonstate = prefs->getBool("/tools/connector/mode");
7422         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7423         g_object_set_data( holder, "mode", act );
7424         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_mode_toggled), holder );
7425     }
7428     {
7429         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
7430                                           _("Avoid"),
7431                                           _("Make connectors avoid selected objects"),
7432                                           INKSCAPE_ICON_CONNECTOR_AVOID,
7433                                           secondarySize );
7434         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7435         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7436     }
7438     {
7439         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7440                                           _("Ignore"),
7441                                           _("Make connectors ignore selected objects"),
7442                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7443                                           secondarySize );
7444         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7445         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7446     }
7448     // Orthogonal connectors toggle button
7449     {
7450         InkToggleAction* act = ink_toggle_action_new( "ConnectorOrthogonalAction",
7451                                                       _("Orthogonal"),
7452                                                       _("Make connector orthogonal or polyline"),
7453                                                       INKSCAPE_ICON_CONNECTOR_ORTHOGONAL,
7454                                                       Inkscape::ICON_SIZE_DECORATION );
7455         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7457         bool tbuttonstate = prefs->getBool("/tools/connector/orthogonal");
7458         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7459         g_object_set_data( holder, "orthogonal", act );
7460         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_connector_orthogonal_toggled), holder );
7461     }
7463     EgeAdjustmentAction* eact = 0;
7464     // Curvature spinbox
7465     eact = create_adjustment_action( "ConnectorCurvatureAction",
7466                                     _("Connector Curvature"), _("Curvature:"),
7467                                     _("The amount of connectors curvature"),
7468                                     "/tools/connector/curvature", defaultConnCurvature,
7469                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-curvature",
7470                                     0, 100, 1.0, 10.0,
7471                                     0, 0, 0,
7472                                     connector_curvature_changed, 1, 0 );
7473     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7475     // Spacing spinbox
7476     eact = create_adjustment_action( "ConnectorSpacingAction",
7477                                     _("Connector Spacing"), _("Spacing:"),
7478                                     _("The amount of space left around objects by auto-routing connectors"),
7479                                     "/tools/connector/spacing", defaultConnSpacing,
7480                                     GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7481                                     0, 100, 1.0, 10.0,
7482                                     0, 0, 0,
7483                                     connector_spacing_changed, 1, 0 );
7484     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7486     // Graph (connector network) layout
7487     {
7488         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7489                                           _("Graph"),
7490                                           _("Nicely arrange selected connector network"),
7491                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7492                                           secondarySize );
7493         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7494         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7495     }
7497     // Default connector length spinbox
7498     eact = create_adjustment_action( "ConnectorLengthAction",
7499                                      _("Connector Length"), _("Length:"),
7500                                      _("Ideal length for connectors when layout is applied"),
7501                                      "/tools/connector/length", 100,
7502                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7503                                      10, 1000, 10.0, 100.0,
7504                                      0, 0, 0,
7505                                      connector_length_changed, 1, 0 );
7506     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7509     // Directed edges toggle button
7510     {
7511         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7512                                                       _("Downwards"),
7513                                                       _("Make connectors with end-markers (arrows) point downwards"),
7514                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7515                                                       Inkscape::ICON_SIZE_DECORATION );
7516         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7518         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7519         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7521         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7522         sigc::connection *connection = new sigc::connection(sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_connector_toolbox_selection_changed), (GObject *)holder))
7523         );
7524     }
7526     // Avoid overlaps toggle button
7527     {
7528         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7529                                                       _("Remove overlaps"),
7530                                                       _("Do not allow overlapping shapes"),
7531                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7532                                                       Inkscape::ICON_SIZE_DECORATION );
7533         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7535         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7536         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7538         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7539     }
7542     // New connection point button
7543     {
7544         InkAction* inky = ink_action_new( "ConnectorNewConnPointAction",
7545                                           _("New connection point"),
7546                                           _("Add a new connection point to the currently selected item"),
7547                                           INKSCAPE_ICON_CONNECTOR_NEW_CONNPOINT,
7548                                           secondarySize );
7549         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_new_connection_point), holder );
7550         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7551     }
7553     // Remove selected connection point button
7555     {
7556         InkAction* inky = ink_action_new( "ConnectorRemoveConnPointAction",
7557                                           _("Remove connection point"),
7558                                           _("Remove the currently selected connection point"),
7559                                           INKSCAPE_ICON_CONNECTOR_REMOVE_CONNPOINT,
7560                                           secondarySize );
7561         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_remove_connection_point), holder );
7562         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7563     }
7566     // Code to watch for changes to the connector-spacing attribute in
7567     // the XML.
7568     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7569     g_assert(repr != NULL);
7571     purge_repr_listener( holder, holder );
7573     if (repr) {
7574         g_object_set_data( holder, "repr", repr );
7575         Inkscape::GC::anchor(repr);
7576         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7577         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7578     }
7579 } // end of sp_connector_toolbox_prep()
7582 //#########################
7583 //##     Paintbucket     ##
7584 //#########################
7586 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7588     gint channels = ege_select_one_action_get_active( act );
7589     flood_channels_set_channels( channels );
7592 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7594     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7595     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7598 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7600     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7601     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7604 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7606     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7607     SPUnit const *unit = tracker->getActiveUnit();
7608     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7610     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7611     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7614 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7616     // FIXME: make defaults settable via Inkscape Options
7617     struct KeyValue {
7618         char const *key;
7619         double value;
7620     } const key_values[] = {
7621         {"threshold", 15},
7622         {"offset", 0.0}
7623     };
7625     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7626         KeyValue const &kv = key_values[i];
7627         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7628         if ( adj ) {
7629             gtk_adjustment_set_value(adj, kv.value);
7630         }
7631     }
7633     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7634     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7635     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7636     ege_select_one_action_set_active( autogap_action, 0 );
7639 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7641     EgeAdjustmentAction* eact = 0;
7642     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7644     {
7645         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7647         GList* items = 0;
7648         gint count = 0;
7649         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7650         {
7651             GtkTreeIter iter;
7652             gtk_list_store_append( model, &iter );
7653             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7654             count++;
7655         }
7656         g_list_free( items );
7657         items = 0;
7658         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7659         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7660         ege_select_one_action_set_appearance( act1, "compact" );
7661         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7662         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7663         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7664         g_object_set_data( holder, "channels_action", act1 );
7665     }
7667     // Spacing spinbox
7668     {
7669         eact = create_adjustment_action(
7670             "ThresholdAction",
7671             _("Fill Threshold"), _("Threshold:"),
7672             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7673             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7674             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7675             0, 0, 0,
7676             paintbucket_threshold_changed, 1, 0 );
7678         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7679         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7680     }
7682     // Create the units menu.
7683     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7684     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7685     if (!stored_unit.empty())
7686         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7687     g_object_set_data( holder, "tracker", tracker );
7688     {
7689         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7690         gtk_action_group_add_action( mainActions, act );
7691     }
7693     // Offset spinbox
7694     {
7695         eact = create_adjustment_action(
7696             "OffsetAction",
7697             _("Grow/shrink by"), _("Grow/shrink by:"),
7698             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7699             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7700             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7701             0, 0, 0,
7702             paintbucket_offset_changed, 1, 2);
7703         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7705         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7706     }
7708     /* Auto Gap */
7709     {
7710         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7712         GList* items = 0;
7713         gint count = 0;
7714         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7715         {
7716             GtkTreeIter iter;
7717             gtk_list_store_append( model, &iter );
7718             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7719             count++;
7720         }
7721         g_list_free( items );
7722         items = 0;
7723         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7724         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7725         ege_select_one_action_set_appearance( act2, "compact" );
7726         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7727         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7728         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7729         g_object_set_data( holder, "autogap_action", act2 );
7730     }
7732     /* Reset */
7733     {
7734         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7735                                           _("Defaults"),
7736                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7737                                           GTK_STOCK_CLEAR );
7738         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7739         gtk_action_group_add_action( mainActions, act );
7740         gtk_action_set_sensitive( act, TRUE );
7741     }
7745 /*
7746   Local Variables:
7747   mode:c++
7748   c-file-style:"stroustrup"
7749   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7750   indent-tabs-mode:nil
7751   fill-column:99
7752   End:
7753 */
7754 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :