Code

Merging in from trunk
[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 "../ui/dialog/calligraphic-profile-rename.h"
90 #include "../ui/icon-names.h"
91 #include "../ui/widget/style-swatch.h"
92 #include "../verbs.h"
93 #include "../widgets/button.h"
94 #include "../widgets/spinbutton-events.h"
95 #include "../widgets/spw-utilities.h"
96 #include "../widgets/widget-sizes.h"
97 #include "../xml/attribute-record.h"
98 #include "../xml/node-event-vector.h"
99 #include "../xml/repr.h"
101 #include "toolbox.h"
103 using Inkscape::UnitTracker;
105 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
106 typedef void (*UpdateFunction)(SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
108 static void       sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
109 static void       sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
110 static void       sp_zoom_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
111 static void       sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
112 static void       sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
113 static void       sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
114 static void       box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
115 static void       sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
116 static void       sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
117 static void       sp_pen_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
118 static void       sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
119 static void       sp_dropper_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
120 static GtkWidget *sp_empty_toolbox_new(SPDesktop *desktop);
121 static void       sp_connector_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
122 static void       sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
123 static void       sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
124 static void       sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
126 namespace { GtkWidget *sp_text_toolbox_new (SPDesktop *desktop); }
129 Inkscape::IconSize prefToSize( Glib::ustring const &path, int base ) {
130     static Inkscape::IconSize sizeChoices[] = {
131         Inkscape::ICON_SIZE_LARGE_TOOLBAR,
132         Inkscape::ICON_SIZE_SMALL_TOOLBAR,
133         Inkscape::ICON_SIZE_MENU
134     };
135     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
136     int index = prefs->getIntLimited( path, base, 0, G_N_ELEMENTS(sizeChoices) );
137     return sizeChoices[index];
140 static struct {
141     gchar const *type_name;
142     gchar const *data_name;
143     sp_verb_t verb;
144     sp_verb_t doubleclick_verb;
145 } const tools[] = {
146     { "SPSelectContext",   "select_tool",    SP_VERB_CONTEXT_SELECT,  SP_VERB_CONTEXT_SELECT_PREFS},
147     { "SPNodeContext",     "node_tool",      SP_VERB_CONTEXT_NODE, SP_VERB_CONTEXT_NODE_PREFS },
148     { "SPTweakContext",    "tweak_tool",     SP_VERB_CONTEXT_TWEAK, SP_VERB_CONTEXT_TWEAK_PREFS },
149     { "SPZoomContext",     "zoom_tool",      SP_VERB_CONTEXT_ZOOM, SP_VERB_CONTEXT_ZOOM_PREFS },
150     { "SPRectContext",     "rect_tool",      SP_VERB_CONTEXT_RECT, SP_VERB_CONTEXT_RECT_PREFS },
151     { "Box3DContext",      "3dbox_tool",     SP_VERB_CONTEXT_3DBOX, SP_VERB_CONTEXT_3DBOX_PREFS },
152     { "SPArcContext",      "arc_tool",       SP_VERB_CONTEXT_ARC, SP_VERB_CONTEXT_ARC_PREFS },
153     { "SPStarContext",     "star_tool",      SP_VERB_CONTEXT_STAR, SP_VERB_CONTEXT_STAR_PREFS },
154     { "SPSpiralContext",   "spiral_tool",    SP_VERB_CONTEXT_SPIRAL, SP_VERB_CONTEXT_SPIRAL_PREFS },
155     { "SPPencilContext",   "pencil_tool",    SP_VERB_CONTEXT_PENCIL, SP_VERB_CONTEXT_PENCIL_PREFS },
156     { "SPPenContext",      "pen_tool",       SP_VERB_CONTEXT_PEN, SP_VERB_CONTEXT_PEN_PREFS },
157     { "SPDynaDrawContext", "dyna_draw_tool", SP_VERB_CONTEXT_CALLIGRAPHIC, SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS },
158     { "SPLPEToolContext",  "lpetool_tool",   SP_VERB_CONTEXT_LPETOOL, SP_VERB_CONTEXT_LPETOOL_PREFS },
159     { "SPEraserContext",   "eraser_tool",    SP_VERB_CONTEXT_ERASER, SP_VERB_CONTEXT_ERASER_PREFS },
160     { "SPFloodContext",    "paintbucket_tool",     SP_VERB_CONTEXT_PAINTBUCKET, SP_VERB_CONTEXT_PAINTBUCKET_PREFS },
161     { "SPTextContext",     "text_tool",      SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
162     { "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
163     { "SPGradientContext", "gradient_tool",  SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
164     { "SPDropperContext",  "dropper_tool",   SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
165     { NULL, NULL, 0, 0 }
166 };
168 static struct {
169     gchar const *type_name;
170     gchar const *data_name;
171     GtkWidget *(*create_func)(SPDesktop *desktop);
172     void (*prep_func)(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder);
173     gchar const *ui_name;
174     gint swatch_verb_id;
175     gchar const *swatch_tool;
176     gchar const *swatch_tip;
177 } const aux_toolboxes[] = {
178     { "SPSelectContext", "select_toolbox", 0, sp_select_toolbox_prep,            "SelectToolbar",
179       SP_VERB_INVALID, 0, 0},
180     { "SPNodeContext",   "node_toolbox",   0, sp_node_toolbox_prep,              "NodeToolbar",
181       SP_VERB_INVALID, 0, 0},
182     { "SPTweakContext",   "tweak_toolbox",   0, sp_tweak_toolbox_prep,              "TweakToolbar",
183       SP_VERB_CONTEXT_TWEAK_PREFS, "/tools/tweak", N_("Color/opacity used for color tweaking")},
184     { "SPZoomContext",   "zoom_toolbox",   0, sp_zoom_toolbox_prep,              "ZoomToolbar",
185       SP_VERB_INVALID, 0, 0},
186     { "SPStarContext",   "star_toolbox",   0, sp_star_toolbox_prep,              "StarToolbar",
187       SP_VERB_CONTEXT_STAR_PREFS,   "/tools/shapes/star",     N_("Style of new stars")},
188     { "SPRectContext",   "rect_toolbox",   0, sp_rect_toolbox_prep,              "RectToolbar",
189       SP_VERB_CONTEXT_RECT_PREFS,   "/tools/shapes/rect",     N_("Style of new rectangles")},
190     { "Box3DContext",  "3dbox_toolbox",  0, box3d_toolbox_prep,             "3DBoxToolbar",
191       SP_VERB_CONTEXT_3DBOX_PREFS,  "/tools/shapes/3dbox",    N_("Style of new 3D boxes")},
192     { "SPArcContext",    "arc_toolbox",    0, sp_arc_toolbox_prep,               "ArcToolbar",
193       SP_VERB_CONTEXT_ARC_PREFS,    "/tools/shapes/arc",      N_("Style of new ellipses")},
194     { "SPSpiralContext", "spiral_toolbox", 0, sp_spiral_toolbox_prep,            "SpiralToolbar",
195       SP_VERB_CONTEXT_SPIRAL_PREFS, "/tools/shapes/spiral",   N_("Style of new spirals")},
196     { "SPPencilContext", "pencil_toolbox", 0, sp_pencil_toolbox_prep,            "PencilToolbar",
197       SP_VERB_CONTEXT_PENCIL_PREFS, "/tools/freehand/pencil", N_("Style of new paths created by Pencil")},
198     { "SPPenContext", "pen_toolbox", 0, sp_pen_toolbox_prep,                     "PenToolbar",
199       SP_VERB_CONTEXT_PEN_PREFS,    "/tools/freehand/pen",    N_("Style of new paths created by Pen")},
200     { "SPDynaDrawContext", "calligraphy_toolbox", 0, sp_calligraphy_toolbox_prep,"CalligraphyToolbar",
201       SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS, "/tools/calligraphic", N_("Style of new calligraphic strokes")},
202     { "SPEraserContext", "eraser_toolbox", 0, sp_eraser_toolbox_prep,"EraserToolbar",
203       SP_VERB_CONTEXT_ERASER_PREFS, "/tools/eraser", _("TBD")},
204     { "SPLPEToolContext", "lpetool_toolbox", 0, sp_lpetool_toolbox_prep, "LPEToolToolbar",
205       SP_VERB_CONTEXT_LPETOOL_PREFS, "/tools/lpetool", _("TBD")},
206     { "SPTextContext",   "text_toolbox",   sp_text_toolbox_new, 0,               0,
207       SP_VERB_INVALID, 0, 0},
208     { "SPDropperContext", "dropper_toolbox", 0, sp_dropper_toolbox_prep,         "DropperToolbar",
209       SP_VERB_INVALID, 0, 0},
210     { "SPGradientContext", "gradient_toolbox", sp_gradient_toolbox_new, 0,       0,
211       SP_VERB_INVALID, 0, 0},
212     { "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep,   "ConnectorToolbar",
213       SP_VERB_INVALID, 0, 0},
214     { "SPFloodContext",  "paintbucket_toolbox",  0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
215       SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
216     { NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
217 };
219 #define TOOLBAR_SLIDER_HINT "full"
221 static gchar const * ui_descr =
222         "<ui>"
223         "  <toolbar name='SelectToolbar'>"
224         "    <toolitem action='EditSelectAll' />"
225         "    <toolitem action='EditSelectAllInAllLayers' />"
226         "    <toolitem action='EditDeselect' />"
227         "    <separator />"
228         "    <toolitem action='ObjectRotate90CCW' />"
229         "    <toolitem action='ObjectRotate90' />"
230         "    <toolitem action='ObjectFlipHorizontally' />"
231         "    <toolitem action='ObjectFlipVertically' />"
232         "    <separator />"
233         "    <toolitem action='SelectionToBack' />"
234         "    <toolitem action='SelectionLower' />"
235         "    <toolitem action='SelectionRaise' />"
236         "    <toolitem action='SelectionToFront' />"
237         "    <separator />"
238         "    <toolitem action='XAction' />"
239         "    <toolitem action='YAction' />"
240         "    <toolitem action='WidthAction' />"
241         "    <toolitem action='LockAction' />"
242         "    <toolitem action='HeightAction' />"
243         "    <toolitem action='UnitsAction' />"
244         "    <separator />"
245         "    <toolitem action='transform_affect_label' />"
246         "    <toolitem action='transform_stroke' />"
247         "    <toolitem action='transform_corners' />"
248         "    <toolitem action='transform_gradient' />"
249         "    <toolitem action='transform_pattern' />"
250         "  </toolbar>"
252         "  <toolbar name='NodeToolbar'>"
253         "    <toolitem action='NodeInsertAction' />"
254         "    <toolitem action='NodeDeleteAction' />"
255         "    <separator />"
256         "    <toolitem action='NodeJoinAction' />"
257         "    <toolitem action='NodeBreakAction' />"
258         "    <separator />"
259         "    <toolitem action='NodeJoinSegmentAction' />"
260         "    <toolitem action='NodeDeleteSegmentAction' />"
261         "    <separator />"
262         "    <toolitem action='NodeCuspAction' />"
263         "    <toolitem action='NodeSmoothAction' />"
264         "    <toolitem action='NodeSymmetricAction' />"
265         "    <toolitem action='NodeAutoAction' />"
266         "    <separator />"
267         "    <toolitem action='NodeLineAction' />"
268         "    <toolitem action='NodeCurveAction' />"
269         "    <separator />"
270         "    <toolitem action='ObjectToPath' />"
271         "    <toolitem action='StrokeToPath' />"
272         "    <separator />"
273         "    <toolitem action='NodeXAction' />"
274         "    <toolitem action='NodeYAction' />"
275         "    <toolitem action='NodeUnitsAction' />"
276         "    <separator />"
277         "    <toolitem action='ObjectEditClipPathAction' />"
278         "    <toolitem action='ObjectEditMaskPathAction' />"
279         "    <toolitem action='EditNextLPEParameterAction' />"
280         "    <separator />"
281         "    <toolitem action='NodesShowHandlesAction' />"
282         "    <toolitem action='NodesShowHelperpath' />"
283         "  </toolbar>"
285         "  <toolbar name='TweakToolbar'>"
286         "    <toolitem action='TweakWidthAction' />"
287         "    <separator />"
288         "    <toolitem action='TweakForceAction' />"
289         "    <toolitem action='TweakPressureAction' />"
290         "    <separator />"
291         "    <toolitem action='TweakModeAction' />"
292         "    <separator />"
293         "    <toolitem action='TweakFidelityAction' />"
294         "    <separator />"
295         "    <toolitem action='TweakChannelsLabel' />"
296         "    <toolitem action='TweakDoH' />"
297         "    <toolitem action='TweakDoS' />"
298         "    <toolitem action='TweakDoL' />"
299         "    <toolitem action='TweakDoO' />"
300         "  </toolbar>"
302         "  <toolbar name='ZoomToolbar'>"
303         "    <toolitem action='ZoomIn' />"
304         "    <toolitem action='ZoomOut' />"
305         "    <separator />"
306         "    <toolitem action='Zoom1:0' />"
307         "    <toolitem action='Zoom1:2' />"
308         "    <toolitem action='Zoom2:1' />"
309         "    <separator />"
310         "    <toolitem action='ZoomSelection' />"
311         "    <toolitem action='ZoomDrawing' />"
312         "    <toolitem action='ZoomPage' />"
313         "    <toolitem action='ZoomPageWidth' />"
314         "    <separator />"
315         "    <toolitem action='ZoomPrev' />"
316         "    <toolitem action='ZoomNext' />"
317         "  </toolbar>"
319         "  <toolbar name='StarToolbar'>"
320         "    <separator />"
321         "    <toolitem action='StarStateAction' />"
322         "    <separator />"
323         "    <toolitem action='FlatAction' />"
324         "    <separator />"
325         "    <toolitem action='MagnitudeAction' />"
326         "    <toolitem action='SpokeAction' />"
327         "    <toolitem action='RoundednessAction' />"
328         "    <toolitem action='RandomizationAction' />"
329         "    <separator />"
330         "    <toolitem action='StarResetAction' />"
331         "  </toolbar>"
333         "  <toolbar name='RectToolbar'>"
334         "    <toolitem action='RectStateAction' />"
335         "    <toolitem action='RectWidthAction' />"
336         "    <toolitem action='RectHeightAction' />"
337         "    <toolitem action='RadiusXAction' />"
338         "    <toolitem action='RadiusYAction' />"
339         "    <toolitem action='RectUnitsAction' />"
340         "    <separator />"
341         "    <toolitem action='RectResetAction' />"
342         "  </toolbar>"
344         "  <toolbar name='3DBoxToolbar'>"
345         "    <toolitem action='3DBoxAngleXAction' />"
346         "    <toolitem action='3DBoxVPXStateAction' />"
347         "    <separator />"
348         "    <toolitem action='3DBoxAngleYAction' />"
349         "    <toolitem action='3DBoxVPYStateAction' />"
350         "    <separator />"
351         "    <toolitem action='3DBoxAngleZAction' />"
352         "    <toolitem action='3DBoxVPZStateAction' />"
353         "  </toolbar>"
355         "  <toolbar name='SpiralToolbar'>"
356         "    <toolitem action='SpiralStateAction' />"
357         "    <toolitem action='SpiralRevolutionAction' />"
358         "    <toolitem action='SpiralExpansionAction' />"
359         "    <toolitem action='SpiralT0Action' />"
360         "    <separator />"
361         "    <toolitem action='SpiralResetAction' />"
362         "  </toolbar>"
364         "  <toolbar name='PenToolbar'>"
365         "    <toolitem action='FreehandModeActionPen' />"
366         "    <separator />"
367         "    <toolitem action='SetPenShapeAction'/>"
368         "  </toolbar>"
370         "  <toolbar name='PencilToolbar'>"
371         "    <toolitem action='FreehandModeActionPencil' />"
372         "    <separator />"
373         "    <toolitem action='PencilToleranceAction' />"
374         "    <separator />"
375         "    <toolitem action='PencilResetAction' />"
376         "    <separator />"
377         "    <toolitem action='SetPencilShapeAction'/>"
378         "  </toolbar>"
380         "  <toolbar name='CalligraphyToolbar'>"
381         "    <separator />"
382         "    <toolitem action='SetProfileAction'/>"
383         "    <separator />"
384         "    <toolitem action='CalligraphyWidthAction' />"
385         "    <toolitem action='PressureAction' />"
386         "    <toolitem action='TraceAction' />"
387         "    <toolitem action='ThinningAction' />"
388         "    <separator />"
389         "    <toolitem action='AngleAction' />"
390         "    <toolitem action='TiltAction' />"
391         "    <toolitem action='FixationAction' />"
392         "    <separator />"
393         "    <toolitem action='CapRoundingAction' />"
394         "    <separator />"
395         "    <toolitem action='TremorAction' />"
396         "    <toolitem action='WiggleAction' />"
397         "    <toolitem action='MassAction' />"
398         "    <separator />"
399         "  </toolbar>"
401         "  <toolbar name='ArcToolbar'>"
402         "    <toolitem action='ArcStateAction' />"
403         "    <separator />"
404         "    <toolitem action='ArcStartAction' />"
405         "    <toolitem action='ArcEndAction' />"
406         "    <separator />"
407         "    <toolitem action='ArcOpenAction' />"
408         "    <separator />"
409         "    <toolitem action='ArcResetAction' />"
410         "    <separator />"
411         "  </toolbar>"
413         "  <toolbar name='PaintbucketToolbar'>"
414         "    <toolitem action='ChannelsAction' />"
415         "    <separator />"
416         "    <toolitem action='ThresholdAction' />"
417         "    <separator />"
418         "    <toolitem action='OffsetAction' />"
419         "    <toolitem action='PaintbucketUnitsAction' />"
420         "    <separator />"
421         "    <toolitem action='AutoGapAction' />"
422         "    <separator />"
423         "    <toolitem action='PaintbucketResetAction' />"
424         "  </toolbar>"
426         "  <toolbar name='EraserToolbar'>"
427         "    <toolitem action='EraserWidthAction' />"
428         "    <separator />"
429         "    <toolitem action='EraserModeAction' />"
430         "  </toolbar>"
432         "  <toolbar name='LPEToolToolbar'>"
433         "    <toolitem action='LPEToolModeAction' />"
434         "    <separator />"
435         "    <toolitem action='LPEShowBBoxAction' />"
436         "    <toolitem action='LPEBBoxFromSelectionAction' />"
437         "    <separator />"
438         "    <toolitem action='LPELineSegmentAction' />"
439         "    <separator />"
440         "    <toolitem action='LPEMeasuringAction' />"
441         "    <toolitem action='LPEToolUnitsAction' />"
442         "    <separator />"
443         "    <toolitem action='LPEOpenLPEDialogAction' />"
444         "  </toolbar>"
446         "  <toolbar name='DropperToolbar'>"
447         "    <toolitem action='DropperOpacityAction' />"
448         "    <toolitem action='DropperPickAlphaAction' />"
449         "    <toolitem action='DropperSetAlphaAction' />"
450         "  </toolbar>"
452         "  <toolbar name='ConnectorToolbar'>"
453         "    <toolitem action='ConnectorAvoidAction' />"
454         "    <toolitem action='ConnectorIgnoreAction' />"
455         "    <toolitem action='ConnectorSpacingAction' />"
456         "    <toolitem action='ConnectorGraphAction' />"
457         "    <toolitem action='ConnectorLengthAction' />"
458         "    <toolitem action='ConnectorDirectedAction' />"
459         "    <toolitem action='ConnectorOverlapAction' />"
460         "  </toolbar>"
462         "</ui>"
465 static Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop );
467 static void toolbox_set_desktop (GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection*);
469 static void setup_tool_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
470 static void update_tool_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
472 static void setup_aux_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
473 static void update_aux_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
475 static void setup_commands_toolbox (GtkWidget *toolbox, SPDesktop *desktop);
476 static void update_commands_toolbox (SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget *toolbox);
478 GtkWidget * sp_toolbox_button_new_from_verb_with_doubleclick( GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
479                                                               Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
480                                                               Inkscape::UI::View::View *view, GtkTooltips *tt);
482 class VerbAction : public Gtk::Action {
483 public:
484     static Glib::RefPtr<VerbAction> create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
486     virtual ~VerbAction();
487     virtual void set_active(bool active = true);
489 protected:
490     virtual Gtk::Widget* create_menu_item_vfunc();
491     virtual Gtk::Widget* create_tool_item_vfunc();
493     virtual void connect_proxy_vfunc(Gtk::Widget* proxy);
494     virtual void disconnect_proxy_vfunc(Gtk::Widget* proxy);
496     virtual void on_activate();
498 private:
499     Inkscape::Verb* verb;
500     Inkscape::Verb* verb2;
501     Inkscape::UI::View::View *view;
502     GtkTooltips *tooltips;
503     bool active;
505     VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips);
506 };
509 Glib::RefPtr<VerbAction> VerbAction::create(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips)
511     Glib::RefPtr<VerbAction> result;
512     SPAction *action = verb->get_action(view);
513     if ( action ) {
514         //SPAction* action2 = verb2 ? verb2->get_action(view) : 0;
515         result = Glib::RefPtr<VerbAction>(new VerbAction(verb, verb2, view, tooltips));
516     }
518     return result;
521 VerbAction::VerbAction(Inkscape::Verb* verb, Inkscape::Verb* verb2, Inkscape::UI::View::View *view, GtkTooltips *tooltips) :
522     Gtk::Action(Glib::ustring(verb->get_id()), Gtk::StockID(verb->get_image()), Glib::ustring(_(verb->get_name())), Glib::ustring(_(verb->get_tip()))),
523     verb(verb),
524     verb2(verb2),
525     view(view),
526     tooltips(tooltips),
527     active(false)
531 VerbAction::~VerbAction()
535 Gtk::Widget* VerbAction::create_menu_item_vfunc()
537 // First call in to get the icon rendered if present in SVG
538     Gtk::Widget *widget = sp_icon_get_icon( property_stock_id().get_value().get_string(), Inkscape::ICON_SIZE_MENU );
539     delete widget;
540     widget = 0;
542     Gtk::Widget* widg = Gtk::Action::create_menu_item_vfunc();
543 //     g_message("create_menu_item_vfunc() = %p  for '%s'", widg, verb->get_id());
544     return widg;
547 Gtk::Widget* VerbAction::create_tool_item_vfunc()
549 //     Gtk::Widget* widg = Gtk::Action::create_tool_item_vfunc();
550     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
551     GtkWidget* toolbox = 0;
552     GtkWidget *button = sp_toolbox_button_new_from_verb_with_doubleclick( toolbox, toolboxSize,
553                                                                           SP_BUTTON_TYPE_TOGGLE,
554                                                                           verb,
555                                                                           verb2,
556                                                                           view,
557                                                                           tooltips );
558     if ( active ) {
559         sp_button_toggle_set_down( SP_BUTTON(button), active);
560     }
561     gtk_widget_show_all( button );
562     Gtk::Widget* wrapped = Glib::wrap(button);
563     Gtk::ToolItem* holder = Gtk::manage(new Gtk::ToolItem());
564     holder->add(*wrapped);
566 //     g_message("create_tool_item_vfunc() = %p  for '%s'", holder, verb->get_id());
567     return holder;
570 void VerbAction::connect_proxy_vfunc(Gtk::Widget* proxy)
572 //     g_message("connect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
573     Gtk::Action::connect_proxy_vfunc(proxy);
576 void VerbAction::disconnect_proxy_vfunc(Gtk::Widget* proxy)
578 //     g_message("disconnect_proxy_vfunc(%p)  for '%s'", proxy, verb->get_id());
579     Gtk::Action::disconnect_proxy_vfunc(proxy);
582 void VerbAction::set_active(bool active)
584     this->active = active;
585     Glib::SListHandle<Gtk::Widget*> proxies = get_proxies();
586     for ( Glib::SListHandle<Gtk::Widget*>::iterator it = proxies.begin(); it != proxies.end(); ++it ) {
587         Gtk::ToolItem* ti = dynamic_cast<Gtk::ToolItem*>(*it);
588         if (ti) {
589             // *should* have one child that is the SPButton
590             Gtk::Widget* child = ti->get_child();
591             if ( child && SP_IS_BUTTON(child->gobj()) ) {
592                 SPButton* button = SP_BUTTON(child->gobj());
593                 sp_button_toggle_set_down( button, active );
594             }
595         }
596     }
599 void VerbAction::on_activate()
601     if ( verb ) {
602         SPAction *action = verb->get_action(view);
603         if ( action ) {
604             sp_action_perform(action, 0);
605         }
606     }
609 /* Global text entry widgets necessary for update */
610 /* GtkWidget *dropper_rgb_entry,
611           *dropper_opacity_entry ; */
612 // should be made a private member once this is converted to class
614 static void delete_connection(GObject */*obj*/, sigc::connection *connection) {
615     connection->disconnect();
616     delete connection;
619 static void purge_repr_listener( GObject* obj, GObject* tbl )
621     (void)obj;
622     Inkscape::XML::Node* oldrepr = reinterpret_cast<Inkscape::XML::Node *>( g_object_get_data( tbl, "repr" ) );
623     if (oldrepr) { // remove old listener
624         sp_repr_remove_listener_by_data(oldrepr, tbl);
625         Inkscape::GC::release(oldrepr);
626         oldrepr = 0;
627         g_object_set_data( tbl, "repr", NULL );
628     }
631 GtkWidget *
632 sp_toolbox_button_new_from_verb_with_doubleclick(GtkWidget *t, Inkscape::IconSize size, SPButtonType type,
633                                                  Inkscape::Verb *verb, Inkscape::Verb *doubleclick_verb,
634                                                  Inkscape::UI::View::View *view, GtkTooltips *tt)
636     SPAction *action = verb->get_action(view);
637     if (!action) return NULL;
639     SPAction *doubleclick_action;
640     if (doubleclick_verb)
641         doubleclick_action = doubleclick_verb->get_action(view);
642     else
643         doubleclick_action = NULL;
645     /* fixme: Handle sensitive/unsensitive */
646     /* fixme: Implement sp_button_new_from_action */
647     GtkWidget *b = sp_button_new(size, type, action, doubleclick_action, tt);
648     gtk_widget_show(b);
651     unsigned int shortcut = sp_shortcut_get_primary(verb);
652     if (shortcut) {
653         gchar key[256];
654         sp_ui_shortcut_string(shortcut, key);
655         gchar *tip = g_strdup_printf ("%s (%s)", action->tip, key);
656         if ( t ) {
657             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, tip, 0 );
658         }
659         g_free(tip);
660     } else {
661         if ( t ) {
662             gtk_toolbar_append_widget( GTK_TOOLBAR(t), b, action->tip, 0 );
663         }
664     }
666     return b;
670 static void trigger_sp_action( GtkAction* /*act*/, gpointer user_data )
672     SPAction* targetAction = SP_ACTION(user_data);
673     if ( targetAction ) {
674         sp_action_perform( targetAction, NULL );
675     }
678 static void sp_action_action_set_sensitive (SPAction */*action*/, unsigned int sensitive, void *data)
680     if ( data ) {
681         GtkAction* act = GTK_ACTION(data);
682         gtk_action_set_sensitive( act, sensitive );
683     }
686 static SPActionEventVector action_event_vector = {
687     {NULL},
688     NULL,
689     NULL,
690     sp_action_action_set_sensitive,
691     NULL,
692     NULL
693 };
695 static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::View::View* view, Inkscape::IconSize size )
697     GtkAction* act = 0;
699     SPAction* targetAction = verb->get_action(view);
700     InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
701     act = GTK_ACTION(inky);
702     gtk_action_set_sensitive( act, targetAction->sensitive );
704     g_signal_connect( G_OBJECT(inky), "activate", GTK_SIGNAL_FUNC(trigger_sp_action), targetAction );
706     SPAction*rebound = dynamic_cast<SPAction *>( nr_object_ref( dynamic_cast<NRObject *>(targetAction) ) );
707     nr_active_object_add_listener( (NRActiveObject *)rebound, (NRObjectEventVector *)&action_event_vector, sizeof(SPActionEventVector), inky );
709     return act;
712 Glib::RefPtr<Gtk::ActionGroup> create_or_fetch_actions( SPDesktop* desktop )
714     Inkscape::UI::View::View *view = desktop;
715     gint verbsToUse[] = {
716         // disabled until we have icons for them:
717         //find
718         //SP_VERB_EDIT_TILE,
719         //SP_VERB_EDIT_UNTILE,
720         SP_VERB_DIALOG_ALIGN_DISTRIBUTE,
721         SP_VERB_DIALOG_DISPLAY,
722         SP_VERB_DIALOG_FILL_STROKE,
723         SP_VERB_DIALOG_NAMEDVIEW,
724         SP_VERB_DIALOG_TEXT,
725         SP_VERB_DIALOG_XML_EDITOR,
726         SP_VERB_DIALOG_LAYERS,
727         SP_VERB_EDIT_CLONE,
728         SP_VERB_EDIT_COPY,
729         SP_VERB_EDIT_CUT,
730         SP_VERB_EDIT_DUPLICATE,
731         SP_VERB_EDIT_PASTE,
732         SP_VERB_EDIT_REDO,
733         SP_VERB_EDIT_UNDO,
734         SP_VERB_EDIT_UNLINK_CLONE,
735         SP_VERB_FILE_EXPORT,
736         SP_VERB_FILE_IMPORT,
737         SP_VERB_FILE_NEW,
738         SP_VERB_FILE_OPEN,
739         SP_VERB_FILE_PRINT,
740         SP_VERB_FILE_SAVE,
741         SP_VERB_OBJECT_TO_CURVE,
742         SP_VERB_SELECTION_GROUP,
743         SP_VERB_SELECTION_OUTLINE,
744         SP_VERB_SELECTION_UNGROUP,
745         SP_VERB_ZOOM_1_1,
746         SP_VERB_ZOOM_1_2,
747         SP_VERB_ZOOM_2_1,
748         SP_VERB_ZOOM_DRAWING,
749         SP_VERB_ZOOM_IN,
750         SP_VERB_ZOOM_NEXT,
751         SP_VERB_ZOOM_OUT,
752         SP_VERB_ZOOM_PAGE,
753         SP_VERB_ZOOM_PAGE_WIDTH,
754         SP_VERB_ZOOM_PREV,
755         SP_VERB_ZOOM_SELECTION,
756     };
758     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
760     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
761     Glib::RefPtr<Gtk::ActionGroup> mainActions;
762     if ( groups.find(desktop) != groups.end() ) {
763         mainActions = groups[desktop];
764     }
766     if ( !mainActions ) {
767         mainActions = Gtk::ActionGroup::create("main");
768         groups[desktop] = mainActions;
769     }
771     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
772         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
773         if ( verb ) {
774             if (!mainActions->get_action(verb->get_id())) {
775                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
776                 mainActions->add(Glib::wrap(act));
777             }
778         }
779     }
781     if ( !mainActions->get_action("ToolZoom") ) {
782         GtkTooltips *tt = gtk_tooltips_new();
783         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
784             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
785             if ( va ) {
786                 mainActions->add(va);
787                 if ( i == 0 ) {
788                     va->set_active(true);
789                 }
790             }
791         }
792     }
795     return mainActions;
799 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
801     gtk_widget_set_size_request( widget,
802                                  widget->allocation.width,
803                                  widget->allocation.height );
806 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
808     gtk_widget_set_size_request( widget, -1, -1 );
813 GtkWidget *
814 sp_tool_toolbox_new()
816     GtkTooltips *tt = gtk_tooltips_new();
817     GtkWidget* tb = gtk_toolbar_new();
818     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
819     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
821     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
822     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
824     gtk_widget_set_sensitive(tb, FALSE);
826     GtkWidget *hb = gtk_handle_box_new();
827     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
828     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
829     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
831     gtk_container_add(GTK_CONTAINER(hb), tb);
832     gtk_widget_show(GTK_WIDGET(tb));
834     sigc::connection* conn = new sigc::connection;
835     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
837     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
838     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
840     return hb;
843 GtkWidget *
844 sp_aux_toolbox_new()
846     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
848     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
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), GTK_POS_LEFT);
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     return hb;
871 //####################################
872 //# Commands Bar
873 //####################################
875 GtkWidget *
876 sp_commands_toolbox_new()
878     GtkWidget *tb = gtk_toolbar_new();
880     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
881     gtk_widget_set_sensitive(tb, FALSE);
883     GtkWidget *hb = gtk_handle_box_new();
884     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
885     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
886     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
888     gtk_container_add(GTK_CONTAINER(hb), tb);
889     gtk_widget_show(GTK_WIDGET(tb));
891     sigc::connection* conn = new sigc::connection;
892     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
894     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
895     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
897     return hb;
900 GtkWidget *
901 sp_snap_toolbox_new()
903         GtkWidget *tb = gtk_vbox_new(FALSE, 0);
904         gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
905         g_object_set_data(G_OBJECT(tb), "desktop", NULL);
907         //GtkWidget *tb = gtk_toolbar_new();
908     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
910     gtk_widget_set_sensitive(tb, FALSE);
912     GtkWidget *hb = gtk_handle_box_new();
913     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
914     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
915     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
917     gtk_container_add(GTK_CONTAINER(hb), tb);
918     gtk_widget_show(GTK_WIDGET(tb));
920     sigc::connection* conn = new sigc::connection;
921     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
923     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
924     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
926     return hb;
929 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
930                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
931                                                        Glib::ustring const &path, gdouble def,
932                                                        GtkWidget *focusTarget,
933                                                        GtkWidget *us,
934                                                        GObject *dataKludge,
935                                                        gboolean altx, gchar const *altx_mark,
936                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
937                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
938                                                        void (*callback)(GtkAdjustment *, GObject *),
939                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
941     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
942     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
943                                                              lower, upper, step, page, 0 ) );
944     if (us) {
945         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
946     }
948     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
950     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
951     if ( shortLabel ) {
952         g_object_set( act, "short_label", shortLabel, NULL );
953     }
955     if ( (descrCount > 0) && descrLabels && descrValues ) {
956         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
957     }
959     if ( focusTarget ) {
960         ege_adjustment_action_set_focuswidget( act, focusTarget );
961     }
963     if ( altx && altx_mark ) {
964         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
965     }
967     if ( dataKludge ) {
968         // Rather lame, but it's the only place where we need to get the entry name
969         // but we don't have an Entry
970         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
971     }
973     // Using a cast just to make sure we pass in the right kind of function pointer
974     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
976     return act;
980 //####################################
981 //# node editing callbacks
982 //####################################
984 /**
985  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
986  */
987 static ShapeEditor *get_current_shape_editor()
989     if (!SP_ACTIVE_DESKTOP) {
990         return NULL;
991     }
993     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
995     if (!SP_IS_NODE_CONTEXT(event_context)) {
996         return NULL;
997     }
999     return event_context->shape_editor;
1003 void
1004 sp_node_path_edit_add(void)
1006     ShapeEditor *shape_editor = get_current_shape_editor();
1007     if (shape_editor) shape_editor->add_node();
1010 void
1011 sp_node_path_edit_delete(void)
1013     ShapeEditor *shape_editor = get_current_shape_editor();
1014     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1017 void
1018 sp_node_path_edit_delete_segment(void)
1020     ShapeEditor *shape_editor = get_current_shape_editor();
1021     if (shape_editor) shape_editor->delete_segment();
1024 void
1025 sp_node_path_edit_break(void)
1027     ShapeEditor *shape_editor = get_current_shape_editor();
1028     if (shape_editor) shape_editor->break_at_nodes();
1031 void
1032 sp_node_path_edit_join(void)
1034     ShapeEditor *shape_editor = get_current_shape_editor();
1035     if (shape_editor) shape_editor->join_nodes();
1038 void
1039 sp_node_path_edit_join_segment(void)
1041     ShapeEditor *shape_editor = get_current_shape_editor();
1042     if (shape_editor) shape_editor->join_segments();
1045 void
1046 sp_node_path_edit_toline(void)
1048     ShapeEditor *shape_editor = get_current_shape_editor();
1049     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1052 void
1053 sp_node_path_edit_tocurve(void)
1055     ShapeEditor *shape_editor = get_current_shape_editor();
1056     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1059 void
1060 sp_node_path_edit_cusp(void)
1062     ShapeEditor *shape_editor = get_current_shape_editor();
1063     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1066 void
1067 sp_node_path_edit_smooth(void)
1069     ShapeEditor *shape_editor = get_current_shape_editor();
1070     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1073 void
1074 sp_node_path_edit_symmetrical(void)
1076     ShapeEditor *shape_editor = get_current_shape_editor();
1077     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1080 void
1081 sp_node_path_edit_auto(void)
1083     ShapeEditor *shape_editor = get_current_shape_editor();
1084     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1087 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1088     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1089     bool show = gtk_toggle_action_get_active( act );
1090     prefs->setBool("/tools/nodes/show_handles",  show);
1091     ShapeEditor *shape_editor = get_current_shape_editor();
1092     if (shape_editor) shape_editor->show_handles(show);
1095 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1096     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1097     bool show = gtk_toggle_action_get_active( act );
1098     prefs->setBool("/tools/nodes/show_helperpath",  show);
1099     ShapeEditor *shape_editor = get_current_shape_editor();
1100     if (shape_editor) shape_editor->show_helperpath(show);
1103 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1104     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1107 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1108     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1111 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1112     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1115 /* is called when the node selection is modified */
1116 static void
1117 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1119     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1120     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1121     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1122     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1124     // quit if run by the attr_changed listener
1125     if (g_object_get_data( tbl, "freeze" )) {
1126         return;
1127     }
1129     // in turn, prevent listener from responding
1130     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1132     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1133     SPUnit const *unit = tracker->getActiveUnit();
1135     ShapeEditor *shape_editor = get_current_shape_editor();
1136     if (shape_editor && shape_editor->has_nodepath()) {
1137         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1138         int n_selected = 0;
1139         if (nodepath) {
1140             n_selected = nodepath->numSelected();
1141         }
1143         if (n_selected == 0) {
1144             gtk_action_set_sensitive(xact, FALSE);
1145             gtk_action_set_sensitive(yact, FALSE);
1146         } else {
1147             gtk_action_set_sensitive(xact, TRUE);
1148             gtk_action_set_sensitive(yact, TRUE);
1149             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1150             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1152             if (n_selected == 1) {
1153                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1154                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1155                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1156                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1157                 }
1158             } else {
1159                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1160                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1161                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1162                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1163                        selected nodes don't coincide (in this case we use the coordinates of the center
1164                        of the bounding box). So the entries are never set to zero. */
1165                     // FIXME: Maybe we should clear the entry if several nodes are selected
1166                     //        instead of providing a kind of average value
1167                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1168                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1169                 }
1170             }
1171         }
1172     } else {
1173         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1174         gtk_action_set_sensitive(xact, FALSE);
1175         gtk_action_set_sensitive(yact, FALSE);
1176     }
1178     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1181 static void
1182 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1184     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1185     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1187     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1188     SPUnit const *unit = tracker->getActiveUnit();
1190     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1191         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1192     }
1194     // quit if run by the attr_changed listener
1195     if (g_object_get_data( tbl, "freeze" )) {
1196         return;
1197     }
1199     // in turn, prevent listener from responding
1200     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1202     ShapeEditor *shape_editor = get_current_shape_editor();
1203     if (shape_editor && shape_editor->has_nodepath()) {
1204         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1205         if (!strcmp(value_name, "x")) {
1206             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1207         }
1208         if (!strcmp(value_name, "y")) {
1209             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1210         }
1211     }
1213     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1216 static void
1217 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1219     sp_node_path_value_changed(adj, tbl, "x");
1222 static void
1223 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1225     sp_node_path_value_changed(adj, tbl, "y");
1228 void
1229 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1231     {
1232     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1233     SPItem *item = selection->singleItem();
1234     if (item && SP_IS_LPE_ITEM(item)) {
1235        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1236            gtk_action_set_sensitive(w, TRUE);
1237        } else {
1238            gtk_action_set_sensitive(w, FALSE);
1239        }
1240     } else {
1241        gtk_action_set_sensitive(w, FALSE);
1242     }
1243     }
1245     {
1246     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1247     SPItem *item = selection->singleItem();
1248     if (item && item->clip_ref && item->clip_ref->getObject()) {
1249        gtk_action_set_sensitive(w, TRUE);
1250     } else {
1251        gtk_action_set_sensitive(w, FALSE);
1252     }
1253     }
1255     {
1256     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1257     SPItem *item = selection->singleItem();
1258     if (item && item->mask_ref && item->mask_ref->getObject()) {
1259        gtk_action_set_sensitive(w, TRUE);
1260     } else {
1261        gtk_action_set_sensitive(w, FALSE);
1262     }
1263     }
1266 void
1267 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1269     sp_node_toolbox_sel_changed (selection, tbl);
1274 //################################
1275 //##    Node Editing Toolbox    ##
1276 //################################
1278 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1280     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1281     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1282     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1283     g_object_set_data( holder, "tracker", tracker );
1285     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1287     {
1288         InkAction* inky = ink_action_new( "NodeInsertAction",
1289                                           _("Insert node"),
1290                                           _("Insert new nodes into selected segments"),
1291                                           INKSCAPE_ICON_NODE_ADD,
1292                                           secondarySize );
1293         g_object_set( inky, "short_label", _("Insert"), NULL );
1294         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1295         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1296     }
1298     {
1299         InkAction* inky = ink_action_new( "NodeDeleteAction",
1300                                           _("Delete node"),
1301                                           _("Delete selected nodes"),
1302                                           INKSCAPE_ICON_NODE_DELETE,
1303                                           secondarySize );
1304         g_object_set( inky, "short_label", _("Delete"), NULL );
1305         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1306         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1307     }
1309     {
1310         InkAction* inky = ink_action_new( "NodeJoinAction",
1311                                           _("Join endnodes"),
1312                                           _("Join selected endnodes"),
1313                                           INKSCAPE_ICON_NODE_JOIN,
1314                                           secondarySize );
1315         g_object_set( inky, "short_label", _("Join"), NULL );
1316         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1317         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1318     }
1320     {
1321         InkAction* inky = ink_action_new( "NodeBreakAction",
1322                                           _("Break nodes"),
1323                                           _("Break path at selected nodes"),
1324                                           INKSCAPE_ICON_NODE_BREAK,
1325                                           secondarySize );
1326         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1327         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1328     }
1331     {
1332         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1333                                           _("Join with segment"),
1334                                           _("Join selected endnodes with a new segment"),
1335                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1336                                           secondarySize );
1337         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1338         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1339     }
1341     {
1342         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1343                                           _("Delete segment"),
1344                                           _("Delete segment between two non-endpoint nodes"),
1345                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1346                                           secondarySize );
1347         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1348         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1349     }
1351     {
1352         InkAction* inky = ink_action_new( "NodeCuspAction",
1353                                           _("Node Cusp"),
1354                                           _("Make selected nodes corner"),
1355                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1356                                           secondarySize );
1357         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1358         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1359     }
1361     {
1362         InkAction* inky = ink_action_new( "NodeSmoothAction",
1363                                           _("Node Smooth"),
1364                                           _("Make selected nodes smooth"),
1365                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1366                                           secondarySize );
1367         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1368         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1369     }
1371     {
1372         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1373                                           _("Node Symmetric"),
1374                                           _("Make selected nodes symmetric"),
1375                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1376                                           secondarySize );
1377         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1378         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1379     }
1381     {
1382         InkAction* inky = ink_action_new( "NodeAutoAction",
1383                                           _("Node Auto"),
1384                                           _("Make selected nodes auto-smooth"),
1385                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1386                                           secondarySize );
1387         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1388         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1389     }
1391     {
1392         InkAction* inky = ink_action_new( "NodeLineAction",
1393                                           _("Node Line"),
1394                                           _("Make selected segments lines"),
1395                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1396                                           secondarySize );
1397         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1398         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1399     }
1401     {
1402         InkAction* inky = ink_action_new( "NodeCurveAction",
1403                                           _("Node Curve"),
1404                                           _("Make selected segments curves"),
1405                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1406                                           secondarySize );
1407         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1408         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1409     }
1411     {
1412         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1413                                                       _("Show Handles"),
1414                                                       _("Show the Bezier handles of selected nodes"),
1415                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1416                                                       Inkscape::ICON_SIZE_DECORATION );
1417         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1418         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1419         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1420     }
1422     {
1423         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1424                                                       _("Show Outline"),
1425                                                       _("Show the outline of the path"),
1426                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1427                                                       Inkscape::ICON_SIZE_DECORATION );
1428         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1429         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1430         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1431     }
1433     {
1434         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1435                                           _("Next path effect parameter"),
1436                                           _("Show next path effect parameter for editing"),
1437                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1438                                           Inkscape::ICON_SIZE_DECORATION );
1439         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1440         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1441         g_object_set_data( holder, "nodes_lpeedit", inky);
1442     }
1444     {
1445         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1446                                           _("Edit clipping path"),
1447                                           _("Edit the clipping path of the object"),
1448                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1449                                           Inkscape::ICON_SIZE_DECORATION );
1450         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1451         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1452         g_object_set_data( holder, "nodes_clippathedit", inky);
1453     }
1455     {
1456         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1457                                           _("Edit mask path"),
1458                                           _("Edit the mask of the object"),
1459                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1460                                           Inkscape::ICON_SIZE_DECORATION );
1461         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1462         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1463         g_object_set_data( holder, "nodes_maskedit", inky);
1464     }
1466     /* X coord of selected node(s) */
1467     {
1468         EgeAdjustmentAction* eact = 0;
1469         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1470         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1471         eact = create_adjustment_action( "NodeXAction",
1472                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1473                                          "/tools/nodes/Xcoord", 0,
1474                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1475                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1476                                          labels, values, G_N_ELEMENTS(labels),
1477                                          sp_node_path_x_value_changed );
1478         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1479         g_object_set_data( holder, "nodes_x_action", eact );
1480         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1481         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1482     }
1484     /* Y coord of selected node(s) */
1485     {
1486         EgeAdjustmentAction* eact = 0;
1487         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1488         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1489         eact = create_adjustment_action( "NodeYAction",
1490                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1491                                          "/tools/nodes/Ycoord", 0,
1492                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1493                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1494                                          labels, values, G_N_ELEMENTS(labels),
1495                                          sp_node_path_y_value_changed );
1496         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1497         g_object_set_data( holder, "nodes_y_action", eact );
1498         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1499         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1500     }
1502     // add the units menu
1503     {
1504         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1505         gtk_action_group_add_action( mainActions, act );
1506     }
1509     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1511     //watch selection
1512     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1514     sigc::connection *c_selection_changed =
1515         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1516                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1517     pool->add_connection ("selection-changed", c_selection_changed);
1519     sigc::connection *c_selection_modified =
1520         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1521                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1522     pool->add_connection ("selection-modified", c_selection_modified);
1524     sigc::connection *c_subselection_changed =
1525         new sigc::connection (desktop->connectToolSubselectionChanged
1526                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1527     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1529     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1531     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1532 } // end of sp_node_toolbox_prep()
1535 //########################
1536 //##    Zoom Toolbox    ##
1537 //########################
1539 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1541     // no custom GtkAction setup needed
1542 } // end of sp_zoom_toolbox_prep()
1544 void
1545 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1547     toolbox_set_desktop(toolbox,
1548                         desktop,
1549                         setup_tool_toolbox,
1550                         update_tool_toolbox,
1551                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1552                                                                          "event_context_connection")));
1556 void
1557 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1559     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1560                         desktop,
1561                         setup_aux_toolbox,
1562                         update_aux_toolbox,
1563                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1564                                                                          "event_context_connection")));
1567 void
1568 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1570     toolbox_set_desktop(toolbox,
1571                         desktop,
1572                         setup_commands_toolbox,
1573                         update_commands_toolbox,
1574                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1575                                                                          "event_context_connection")));
1578 void
1579 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1581     toolbox_set_desktop(toolbox,
1582                         desktop,
1583                         setup_snap_toolbox,
1584                         update_snap_toolbox,
1585                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1586                                                                          "event_context_connection")));
1590 static void
1591 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1593     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1594     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1596     if (old_desktop) {
1597         GList *children, *iter;
1599         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1600         for ( iter = children ; iter ; iter = iter->next ) {
1601             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1602         }
1603         g_list_free(children);
1604     }
1606     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1608     if (desktop) {
1609         gtk_widget_set_sensitive(toolbox, TRUE);
1610         setup_func(toolbox, desktop);
1611         update_func(desktop, desktop->event_context, toolbox);
1612         *conn = desktop->connectEventContextChanged
1613             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1614     } else {
1615         gtk_widget_set_sensitive(toolbox, FALSE);
1616     }
1618 } // end of toolbox_set_desktop()
1621 static void
1622 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1624     gchar const * descr =
1625         "<ui>"
1626         "  <toolbar name='ToolToolbar'>"
1627         "    <toolitem action='ToolSelector' />"
1628         "    <toolitem action='ToolNode' />"
1629         "    <toolitem action='ToolTweak' />"
1630         "    <toolitem action='ToolZoom' />"
1631         "    <toolitem action='ToolRect' />"
1632         "    <toolitem action='Tool3DBox' />"
1633         "    <toolitem action='ToolArc' />"
1634         "    <toolitem action='ToolStar' />"
1635         "    <toolitem action='ToolSpiral' />"
1636         "    <toolitem action='ToolPencil' />"
1637         "    <toolitem action='ToolPen' />"
1638         "    <toolitem action='ToolCalligraphic' />"
1639         "    <toolitem action='ToolEraser' />"
1640 //        "    <toolitem action='ToolLPETool' />"
1641         "    <toolitem action='ToolPaintBucket' />"
1642         "    <toolitem action='ToolText' />"
1643         "    <toolitem action='ToolConnector' />"
1644         "    <toolitem action='ToolGradient' />"
1645         "    <toolitem action='ToolDropper' />"
1646         "  </toolbar>"
1647         "</ui>";
1648     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1649     GtkUIManager* mgr = gtk_ui_manager_new();
1650     GError* errVal = 0;
1651     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1653     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1654     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1656     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1657     if ( prefs->getBool("/toolbox/icononly", true) ) {
1658         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1659     }
1660     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1661     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1663     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1664     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1666     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1668     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1669     if ( child ) {
1670         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1671     }
1673     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1674 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1678 static void
1679 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1681     gchar const *const tname = ( eventcontext
1682                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1683                                  : NULL );
1684     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1686     for (int i = 0 ; tools[i].type_name ; i++ ) {
1687         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1688         if ( act ) {
1689             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1690             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1691             if ( verbAct ) {
1692                 verbAct->set_active(setActive);
1693             }
1694         }
1695     }
1698 static void
1699 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1701     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1702     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1703     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1704     GtkUIManager* mgr = gtk_ui_manager_new();
1705     GError* errVal = 0;
1706     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1707     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1709     std::map<std::string, GtkWidget*> dataHolders;
1711     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1712         if ( aux_toolboxes[i].prep_func ) {
1713             // converted to GtkActions and UIManager
1715             GtkWidget* kludge = gtk_toolbar_new();
1716             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1717             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1718             dataHolders[aux_toolboxes[i].type_name] = kludge;
1719             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1720         } else {
1722             GtkWidget *sub_toolbox = 0;
1723             if (aux_toolboxes[i].create_func == NULL)
1724                 sub_toolbox = sp_empty_toolbox_new(desktop);
1725             else {
1726                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1727             }
1729             gtk_size_group_add_widget( grouper, sub_toolbox );
1731             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1732             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1734         }
1735     }
1737     // Second pass to create toolbars *after* all GtkActions are created
1738     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1739         if ( aux_toolboxes[i].prep_func ) {
1740             // converted to GtkActions and UIManager
1742             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1744             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1745             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1747             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1748             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1749             g_free( tmp );
1750             tmp = 0;
1752             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1753                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1754             }
1756             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1757             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1759             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1761             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1762                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1763                 swatch->setDesktop( desktop );
1764                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1765                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1766                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1767                 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 );
1768             }
1770             gtk_widget_show_all( holder );
1771             sp_set_font_size_smaller( holder );
1773             gtk_size_group_add_widget( grouper, holder );
1775             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1776             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1777         }
1778     }
1780     g_object_unref( G_OBJECT(grouper) );
1783 static void
1784 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1786     gchar const *tname = ( eventcontext
1787                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1788                            : NULL );
1789     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1790         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1791         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1792             gtk_widget_show_all(sub_toolbox);
1793             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1794         } else {
1795             gtk_widget_hide(sub_toolbox);
1796         }
1797     }
1800 static void
1801 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1803     gchar const * descr =
1804         "<ui>"
1805         "  <toolbar name='CommandsToolbar'>"
1806         "    <toolitem action='FileNew' />"
1807         "    <toolitem action='FileOpen' />"
1808         "    <toolitem action='FileSave' />"
1809         "    <toolitem action='FilePrint' />"
1810         "    <separator />"
1811         "    <toolitem action='FileImport' />"
1812         "    <toolitem action='FileExport' />"
1813         "    <separator />"
1814         "    <toolitem action='EditUndo' />"
1815         "    <toolitem action='EditRedo' />"
1816         "    <separator />"
1817         "    <toolitem action='EditCopy' />"
1818         "    <toolitem action='EditCut' />"
1819         "    <toolitem action='EditPaste' />"
1820         "    <separator />"
1821         "    <toolitem action='ZoomSelection' />"
1822         "    <toolitem action='ZoomDrawing' />"
1823         "    <toolitem action='ZoomPage' />"
1824         "    <separator />"
1825         "    <toolitem action='EditDuplicate' />"
1826         "    <toolitem action='EditClone' />"
1827         "    <toolitem action='EditUnlinkClone' />"
1828         "    <separator />"
1829         "    <toolitem action='SelectionGroup' />"
1830         "    <toolitem action='SelectionUnGroup' />"
1831         "    <separator />"
1832         "    <toolitem action='DialogFillStroke' />"
1833         "    <toolitem action='DialogText' />"
1834         "    <toolitem action='DialogLayers' />"
1835         "    <toolitem action='DialogXMLEditor' />"
1836         "    <toolitem action='DialogAlignDistribute' />"
1837         "    <separator />"
1838         "    <toolitem action='DialogPreferences' />"
1839         "    <toolitem action='DialogDocumentProperties' />"
1840         "  </toolbar>"
1841         "</ui>";
1842     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1843     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1845     GtkUIManager* mgr = gtk_ui_manager_new();
1846     GError* errVal = 0;
1848     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1849     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1851     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1852     if ( prefs->getBool("/toolbox/icononly", true) ) {
1853         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1854     }
1856     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1857     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1859     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1860     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1863     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1865     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1866     if ( child ) {
1867         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1868     }
1870     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1873 static void
1874 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1878 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1880         if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1881                 return;
1882         }
1884         gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1885         g_assert(ptr != NULL);
1887         SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1888         SPNamedView *nv = sp_desktop_namedview(dt);
1889         SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1891         if (dt == NULL || nv == NULL) {
1892                 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1893                 return;
1894         }
1896         Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1898         if (repr == NULL) {
1899                 g_warning("This namedview doesn't have a xml representation attached!");
1900                 return;
1901         }
1903         bool saved = sp_document_get_undo_sensitive(doc);
1904         sp_document_set_undo_sensitive(doc, false);
1906         bool v = false;
1907         SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1909         switch (attr) {
1910         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1911                 dt->toggleSnapGlobal();
1912                 break;
1913         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1914                 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1915                 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1916                 break;
1917         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1918                 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1919                 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1920                 break;
1921         case SP_ATTR_INKSCAPE_BBOX_NODES:
1922                 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1923                 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1924                 break;
1925         case SP_ATTR_INKSCAPE_SNAP_NODES:
1926                 v = nv->snap_manager.snapprefs.getSnapModeNode();
1927                 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1928                 break;
1929         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1930                 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1931                 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1932                 break;
1933         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1934                 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1935                 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1936                 break;
1937         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1938                 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1939                 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1940                 break;
1941         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1942                 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1943                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1944                 break;
1945         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1946                 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1947                 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1948                 break;
1949         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1950                 v = nv->snap_manager.snapprefs.getSnapToGrids();
1951                 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1952                 break;
1953         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1954                 v = nv->snap_manager.snapprefs.getSnapToGuides();
1955                 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1956                 break;
1957         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1958                 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1959                 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1960                 break;
1961         /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1962                 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1963                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1964                 break;*/
1965         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1966                 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1967                 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1968                 break;
1969         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1970                 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1971                 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1972                 break;
1973         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1974                 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1975                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1976                 break;
1977         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1978                 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1979                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1980                 break;
1981         default:
1982                 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1983                 break;
1984         }
1986         // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1987         doc->setModifiedSinceSave();
1989         sp_document_set_undo_sensitive(doc, saved);
1992 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1994         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1995         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1997         gchar const * descr =
1998                 "<ui>"
1999                 "  <toolbar name='SnapToolbar'>"
2000                 "    <toolitem action='ToggleSnapGlobal' />"
2001                         "    <separator />"
2002                         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
2003                         "    <toolitem action='ToggleSnapToBBoxPath' />"
2004                         "    <toolitem action='ToggleSnapToBBoxNode' />"
2005                         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
2006                         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2007                         "    <separator />"
2008                         "    <toolitem action='ToggleSnapFromNode' />"
2009                         "    <toolitem action='ToggleSnapToItemPath' />"
2010                         "    <toolitem action='ToggleSnapToPathIntersections' />"
2011                         "    <toolitem action='ToggleSnapToItemNode' />"
2012                         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2013                         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2014                         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2015                         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2016                         "    <separator />"
2017                         "    <toolitem action='ToggleSnapToPageBorder' />"
2018                         "    <toolitem action='ToggleSnapToGrids' />"
2019                         "    <toolitem action='ToggleSnapToGuides' />"
2020                   //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2021                         "  </toolbar>"
2022                 "</ui>";
2024         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2026         {
2027                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal",
2028                     _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2029                     SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2031                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2032                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2033         }
2035         {
2036                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2037                     _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2038                     secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2040                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2041                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2042         }
2044         {
2045                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2046                     _("Bounding box edges"), _("Snap to edges of a bounding box"),
2047                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2049                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2050                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2051         }
2053         {
2054                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2055                     _("Bounding box corners"), _("Snap to bounding box corners"),
2056                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2058                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2059                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2060         }
2062         {
2063                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2064                     _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2065                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2066                     SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2068                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2069                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2070         }
2072         {
2073                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2074                     _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2075                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2077                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2078                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2079         }
2081         {
2082                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2083                     _("Nodes"), _("Snap nodes or handles"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2085                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2086                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2087         }
2089         {
2090                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2091                     _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2092                     SP_ATTR_INKSCAPE_OBJECT_PATHS);
2094                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2095                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2096         }
2098         {
2099                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2100                     _("Path intersections"), _("Snap to path intersections"),
2101                     INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2103                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2104                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2105         }
2107         {
2108                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2109                     _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2110                     SP_ATTR_INKSCAPE_OBJECT_NODES);
2112                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2113                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2114         }
2116         {
2117                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2118                     _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2119                     secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2121                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2122                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2123         }
2125         {
2126                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2127                     _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2128                     INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2130                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2131                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2132         }
2134         {
2135                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2136                     _("Object Centers"), _("Snap from and to centers of objects"),
2137                     INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2139                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2140                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2141         }
2143         {
2144                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2145                     _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2146                     INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2148                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2149                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2150         }
2152         {
2153                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2154                     _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2155                     secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2157                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2158                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2159         }
2161         {
2162                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2163                     _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2164                     SP_ATTR_INKSCAPE_SNAP_GRIDS);
2166                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2167                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2168         }
2170         {
2171                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2172                     _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2173                     SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2175                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2176                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2177         }
2179         /*{
2180                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2181                     _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2182                     INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2183                     SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2185                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2186                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2187         }*/
2189     GtkUIManager* mgr = gtk_ui_manager_new();
2190     GError* errVal = 0;
2192     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2193     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2195     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2196     if ( prefs->getBool("/toolbox/icononly", true) ) {
2197         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2198     }
2200     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2201     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2203     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2204     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2206     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2208     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2209     if ( child ) {
2210         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2211     }
2213     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2217 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2219         g_assert(desktop != NULL);
2220         g_assert(toolbox != NULL);
2222         SPNamedView *nv = sp_desktop_namedview(desktop);
2223         if (nv == NULL) {
2224                 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2225                 return;
2226         }
2228         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2230         Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2231         Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2232         Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2233         Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2234         Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2235         Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2236         Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2237         Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2238         Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2239         Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2240         Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2241         Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2242         Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2243         Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2244         Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2245         //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2246         Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2247         Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2250         if (!act1) {
2251                 return; // The snap actions haven't been defined yet (might be the case during startup)
2252         }
2254         // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2255         // changes in our document because we're only updating the UI;
2256         // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2257         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2259         bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2260         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2262         bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2263         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2264         gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2266         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2267         gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2268         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2269         gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2270         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2271         gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2272         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2273         gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2275         bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2276         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2277         gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2279         bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2280         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2281         gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2282         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2283         gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2284         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2285         gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2286         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2287         gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2288         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2289         gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2290         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2291         gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2292         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2293         gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2295         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2296         gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2297         //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2298         //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2300         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2301         gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2302         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2303         gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2306         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2309 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2311     gtk_widget_show(toolbox_toplevel);
2312     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2314     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2315     if (!shown_toolbox) {
2316         return;
2317     }
2318     gtk_widget_show(toolbox);
2320     gtk_widget_show_all(shown_toolbox);
2323 static GtkWidget *
2324 sp_empty_toolbox_new(SPDesktop *desktop)
2326     GtkWidget *tbl = gtk_toolbar_new();
2327     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2328     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2330     gtk_widget_show_all(tbl);
2331     sp_set_font_size_smaller (tbl);
2333     return tbl;
2336 #define MODE_LABEL_WIDTH 70
2338 //########################
2339 //##       Star         ##
2340 //########################
2342 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2344     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2346     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2347         // do not remember prefs if this call is initiated by an undo change, because undoing object
2348         // creation sets bogus values to its attributes before it is deleted
2349         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2350         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2351     }
2353     // quit if run by the attr_changed listener
2354     if (g_object_get_data( dataKludge, "freeze" )) {
2355         return;
2356     }
2358     // in turn, prevent listener from responding
2359     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2361     bool modmade = false;
2363     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2364     GSList const *items = selection->itemList();
2365     for (; items != NULL; items = items->next) {
2366         if (SP_IS_STAR((SPItem *) items->data)) {
2367             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2368             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2369             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2370                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2371                                     + M_PI / (gint)adj->value));
2372             SP_OBJECT((SPItem *) items->data)->updateRepr();
2373             modmade = true;
2374         }
2375     }
2376     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2377                                    _("Star: Change number of corners"));
2379     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2382 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2384     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2386     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2387         if (!IS_NAN(adj->value)) {
2388                         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2389                         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2390         }
2391     }
2393     // quit if run by the attr_changed listener
2394     if (g_object_get_data( dataKludge, "freeze" )) {
2395         return;
2396     }
2398     // in turn, prevent listener from responding
2399     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2401     bool modmade = false;
2402     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2403     GSList const *items = selection->itemList();
2404     for (; items != NULL; items = items->next) {
2405         if (SP_IS_STAR((SPItem *) items->data)) {
2406             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2408             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2409             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2410             if (r2 < r1) {
2411                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2412             } else {
2413                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2414             }
2416             SP_OBJECT((SPItem *) items->data)->updateRepr();
2417             modmade = true;
2418         }
2419     }
2421     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2422                                    _("Star: Change spoke ratio"));
2424     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2427 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2429     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2430     bool flat = ege_select_one_action_get_active( act ) == 0;
2432     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2433         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2434         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2435     }
2437     // quit if run by the attr_changed listener
2438     if (g_object_get_data( dataKludge, "freeze" )) {
2439         return;
2440     }
2442     // in turn, prevent listener from responding
2443     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2445     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2446     GSList const *items = selection->itemList();
2447     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2448     bool modmade = false;
2450     if ( prop_action ) {
2451         gtk_action_set_sensitive( prop_action, !flat );
2452     }
2454     for (; items != NULL; items = items->next) {
2455         if (SP_IS_STAR((SPItem *) items->data)) {
2456             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2457             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2458             SP_OBJECT((SPItem *) items->data)->updateRepr();
2459             modmade = true;
2460         }
2461     }
2463     if (modmade) {
2464         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2465                          flat ? _("Make polygon") : _("Make star"));
2466     }
2468     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2471 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2473     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2475     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2476         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2477         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2478     }
2480     // quit if run by the attr_changed listener
2481     if (g_object_get_data( dataKludge, "freeze" )) {
2482         return;
2483     }
2485     // in turn, prevent listener from responding
2486     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2488     bool modmade = false;
2490     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2491     GSList const *items = selection->itemList();
2492     for (; items != NULL; items = items->next) {
2493         if (SP_IS_STAR((SPItem *) items->data)) {
2494             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2495             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2496             SP_OBJECT(items->data)->updateRepr();
2497             modmade = true;
2498         }
2499     }
2500     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2501                                    _("Star: Change rounding"));
2503     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2506 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2508     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2510     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2511         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2512         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2513     }
2515     // quit if run by the attr_changed listener
2516     if (g_object_get_data( dataKludge, "freeze" )) {
2517         return;
2518     }
2520     // in turn, prevent listener from responding
2521     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2523     bool modmade = false;
2525     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2526     GSList const *items = selection->itemList();
2527     for (; items != NULL; items = items->next) {
2528         if (SP_IS_STAR((SPItem *) items->data)) {
2529             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2530             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2531             SP_OBJECT(items->data)->updateRepr();
2532             modmade = true;
2533         }
2534     }
2535     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2536                                    _("Star: Change randomization"));
2538     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2542 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2543                                        gchar const */*old_value*/, gchar const */*new_value*/,
2544                                        bool /*is_interactive*/, gpointer data)
2546     GtkWidget *tbl = GTK_WIDGET(data);
2548     // quit if run by the _changed callbacks
2549     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2550         return;
2551     }
2553     // in turn, prevent callbacks from responding
2554     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2556     GtkAdjustment *adj = 0;
2558     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2559     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2561     if (!strcmp(name, "inkscape:randomized")) {
2562         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2563         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2564     } else if (!strcmp(name, "inkscape:rounded")) {
2565         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2566         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2567     } else if (!strcmp(name, "inkscape:flatsided")) {
2568         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2569         char const *flatsides = repr->attribute("inkscape:flatsided");
2570         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2571         if ( flatsides && !strcmp(flatsides,"false") ) {
2572             ege_select_one_action_set_active( flat_action, 1 );
2573             gtk_action_set_sensitive( prop_action, TRUE );
2574         } else {
2575             ege_select_one_action_set_active( flat_action, 0 );
2576             gtk_action_set_sensitive( prop_action, FALSE );
2577         }
2578     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2579         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2580         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2581         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2582         if (r2 < r1) {
2583             gtk_adjustment_set_value(adj, r2/r1);
2584         } else {
2585             gtk_adjustment_set_value(adj, r1/r2);
2586         }
2587     } else if (!strcmp(name, "sodipodi:sides")) {
2588         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2589         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2590     }
2592     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2596 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2598     NULL, /* child_added */
2599     NULL, /* child_removed */
2600     star_tb_event_attr_changed,
2601     NULL, /* content_changed */
2602     NULL  /* order_changed */
2603 };
2606 /**
2607  *  \param selection Should not be NULL.
2608  */
2609 static void
2610 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2612     int n_selected = 0;
2613     Inkscape::XML::Node *repr = NULL;
2615     purge_repr_listener( tbl, tbl );
2617     for (GSList const *items = selection->itemList();
2618          items != NULL;
2619          items = items->next)
2620     {
2621         if (SP_IS_STAR((SPItem *) items->data)) {
2622             n_selected++;
2623             repr = SP_OBJECT_REPR((SPItem *) items->data);
2624         }
2625     }
2627     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2629     if (n_selected == 0) {
2630         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2631     } else if (n_selected == 1) {
2632         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2634         if (repr) {
2635             g_object_set_data( tbl, "repr", repr );
2636             Inkscape::GC::anchor(repr);
2637             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2638             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2639         }
2640     } else {
2641         // FIXME: implement averaging of all parameters for multiple selected stars
2642         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2643         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2644     }
2648 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2650     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2651     // callbacks to lump all the changes for all selected objects in one undo step
2653     GtkAdjustment *adj = 0;
2655     // fixme: make settable in prefs!
2656     gint mag = 5;
2657     gdouble prop = 0.5;
2658     gboolean flat = FALSE;
2659     gdouble randomized = 0;
2660     gdouble rounded = 0;
2662     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2663     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2665     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2666     gtk_action_set_sensitive( sb2, !flat );
2668     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2669     gtk_adjustment_set_value(adj, mag);
2670     gtk_adjustment_value_changed(adj);
2672     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2673     gtk_adjustment_set_value(adj, prop);
2674     gtk_adjustment_value_changed(adj);
2676     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2677     gtk_adjustment_set_value(adj, rounded);
2678     gtk_adjustment_value_changed(adj);
2680     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2681     gtk_adjustment_set_value(adj, randomized);
2682     gtk_adjustment_value_changed(adj);
2686 void
2687 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2689     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2690     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2691     GtkWidget *l = gtk_label_new(NULL);
2692     gtk_label_set_markup(GTK_LABEL(l), title);
2693     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2694     if ( GTK_IS_TOOLBAR(tbl) ) {
2695         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2696     } else {
2697         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2698     }
2699     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2703 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2705     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2707     {
2708         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2709         ege_output_action_set_use_markup( act, TRUE );
2710         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2711         g_object_set_data( holder, "mode_action", act );
2712     }
2714     {
2715         EgeAdjustmentAction* eact = 0;
2716         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2717         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2719         /* Flatsided checkbox */
2720         {
2721             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2723             GtkTreeIter iter;
2724             gtk_list_store_append( model, &iter );
2725             gtk_list_store_set( model, &iter,
2726                                 0, _("Polygon"),
2727                                 1, _("Regular polygon (with one handle) instead of a star"),
2728                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2729                                 -1 );
2731             gtk_list_store_append( model, &iter );
2732             gtk_list_store_set( model, &iter,
2733                                 0, _("Star"),
2734                                 1, _("Star instead of a regular polygon (with one handle)"),
2735                                 2, INKSCAPE_ICON_DRAW_STAR,
2736                                 -1 );
2738             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2739             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2740             g_object_set_data( holder, "flat_action", act );
2742             ege_select_one_action_set_appearance( act, "full" );
2743             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2744             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2745             ege_select_one_action_set_icon_column( act, 2 );
2746             ege_select_one_action_set_icon_size( act, secondarySize );
2747             ege_select_one_action_set_tooltip_column( act, 1  );
2749             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2750             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2751         }
2753         /* Magnitude */
2754         {
2755         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2756         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2757         eact = create_adjustment_action( "MagnitudeAction",
2758                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2759                                          "/tools/shapes/star/magnitude", 3,
2760                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2761                                          3, 1024, 1, 5,
2762                                          labels, values, G_N_ELEMENTS(labels),
2763                                          sp_stb_magnitude_value_changed,
2764                                          1.0, 0 );
2765         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2766         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2767         }
2769         /* Spoke ratio */
2770         {
2771         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2772         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2773         eact = create_adjustment_action( "SpokeAction",
2774                                          _("Spoke ratio"), _("Spoke ratio:"),
2775                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2776                                          // Base radius is the same for the closest handle.
2777                                          _("Base radius to tip radius ratio"),
2778                                          "/tools/shapes/star/proportion", 0.5,
2779                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2780                                          0.01, 1.0, 0.01, 0.1,
2781                                          labels, values, G_N_ELEMENTS(labels),
2782                                          sp_stb_proportion_value_changed );
2783         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2784         g_object_set_data( holder, "prop_action", eact );
2785         }
2787         if ( !isFlatSided ) {
2788             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2789         } else {
2790             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2791         }
2793         /* Roundedness */
2794         {
2795         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2796         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2797         eact = create_adjustment_action( "RoundednessAction",
2798                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2799                                          "/tools/shapes/star/rounded", 0.0,
2800                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2801                                          -10.0, 10.0, 0.01, 0.1,
2802                                          labels, values, G_N_ELEMENTS(labels),
2803                                          sp_stb_rounded_value_changed );
2804         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2805         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2806         }
2808         /* Randomization */
2809         {
2810         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2811         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2812         eact = create_adjustment_action( "RandomizationAction",
2813                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2814                                          "/tools/shapes/star/randomized", 0.0,
2815                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2816                                          -10.0, 10.0, 0.001, 0.01,
2817                                          labels, values, G_N_ELEMENTS(labels),
2818                                          sp_stb_randomized_value_changed, 0.1, 3 );
2819         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2820         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2821         }
2822     }
2824     {
2825         /* Reset */
2826         {
2827             GtkAction* act = gtk_action_new( "StarResetAction",
2828                                              _("Defaults"),
2829                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2830                                              GTK_STOCK_CLEAR );
2831             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2832             gtk_action_group_add_action( mainActions, act );
2833             gtk_action_set_sensitive( act, TRUE );
2834         }
2835     }
2837     sigc::connection *connection = new sigc::connection(
2838         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2839         );
2840     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2841     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2845 //########################
2846 //##       Rect         ##
2847 //########################
2849 static void sp_rtb_sensitivize( GObject *tbl )
2851     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2852     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2853     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2855     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2856         gtk_action_set_sensitive( not_rounded, FALSE );
2857     } else {
2858         gtk_action_set_sensitive( not_rounded, TRUE );
2859     }
2863 static void
2864 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2865                           void (*setter)(SPRect *, gdouble))
2867     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2869     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2870     SPUnit const *unit = tracker->getActiveUnit();
2872     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2873         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2874         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2875     }
2877     // quit if run by the attr_changed listener
2878     if (g_object_get_data( tbl, "freeze" )) {
2879         return;
2880     }
2882     // in turn, prevent listener from responding
2883     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2885     bool modmade = false;
2886     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2887     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2888         if (SP_IS_RECT(items->data)) {
2889             if (adj->value != 0) {
2890                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2891             } else {
2892                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2893             }
2894             modmade = true;
2895         }
2896     }
2898     sp_rtb_sensitivize( tbl );
2900     if (modmade) {
2901         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2902                                    _("Change rectangle"));
2903     }
2905     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2908 static void
2909 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2911     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2914 static void
2915 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2917     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2920 static void
2921 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2923     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2926 static void
2927 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2929     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2934 static void
2935 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2937     GtkAdjustment *adj = 0;
2939     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2940     gtk_adjustment_set_value(adj, 0.0);
2941     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2942     gtk_adjustment_value_changed(adj);
2944     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2945     gtk_adjustment_set_value(adj, 0.0);
2946     gtk_adjustment_value_changed(adj);
2948     sp_rtb_sensitivize( obj );
2951 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2952                                        gchar const */*old_value*/, gchar const */*new_value*/,
2953                                        bool /*is_interactive*/, gpointer data)
2955     GObject *tbl = G_OBJECT(data);
2957     // quit if run by the _changed callbacks
2958     if (g_object_get_data( tbl, "freeze" )) {
2959         return;
2960     }
2962     // in turn, prevent callbacks from responding
2963     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2965     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2966     SPUnit const *unit = tracker->getActiveUnit();
2968     gpointer item = g_object_get_data( tbl, "item" );
2969     if (item && SP_IS_RECT(item)) {
2970         {
2971             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2972             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2973             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2974         }
2976         {
2977             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2978             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2979             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2980         }
2982         {
2983             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2984             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2985             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2986         }
2988         {
2989             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2990             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2991             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2992         }
2993     }
2995     sp_rtb_sensitivize( tbl );
2997     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3001 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
3002     NULL, /* child_added */
3003     NULL, /* child_removed */
3004     rect_tb_event_attr_changed,
3005     NULL, /* content_changed */
3006     NULL  /* order_changed */
3007 };
3009 /**
3010  *  \param selection should not be NULL.
3011  */
3012 static void
3013 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3015     int n_selected = 0;
3016     Inkscape::XML::Node *repr = NULL;
3017     SPItem *item = NULL;
3019     if ( g_object_get_data( tbl, "repr" ) ) {
3020         g_object_set_data( tbl, "item", NULL );
3021     }
3022     purge_repr_listener( tbl, tbl );
3024     for (GSList const *items = selection->itemList();
3025          items != NULL;
3026          items = items->next) {
3027         if (SP_IS_RECT((SPItem *) items->data)) {
3028             n_selected++;
3029             item = (SPItem *) items->data;
3030             repr = SP_OBJECT_REPR(item);
3031         }
3032     }
3034     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3036     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3038     if (n_selected == 0) {
3039         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3041         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3042         gtk_action_set_sensitive(w, FALSE);
3043         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3044         gtk_action_set_sensitive(h, FALSE);
3046     } else if (n_selected == 1) {
3047         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3048         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3050         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3051         gtk_action_set_sensitive(w, TRUE);
3052         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3053         gtk_action_set_sensitive(h, TRUE);
3055         if (repr) {
3056             g_object_set_data( tbl, "repr", repr );
3057             g_object_set_data( tbl, "item", item );
3058             Inkscape::GC::anchor(repr);
3059             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3060             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3061         }
3062     } else {
3063         // FIXME: implement averaging of all parameters for multiple selected
3064         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3065         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3066         sp_rtb_sensitivize( tbl );
3067     }
3071 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3073     EgeAdjustmentAction* eact = 0;
3074     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3076     {
3077         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3078         ege_output_action_set_use_markup( act, TRUE );
3079         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3080         g_object_set_data( holder, "mode_action", act );
3081     }
3083     // rx/ry units menu: create
3084     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3085     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3086     // fixme: add % meaning per cent of the width/height
3087     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3088     g_object_set_data( holder, "tracker", tracker );
3090     /* W */
3091     {
3092         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3093         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3094         eact = create_adjustment_action( "RectWidthAction",
3095                                          _("Width"), _("W:"), _("Width of rectangle"),
3096                                          "/tools/shapes/rect/width", 0,
3097                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3098                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3099                                          labels, values, G_N_ELEMENTS(labels),
3100                                          sp_rtb_width_value_changed );
3101         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3102         g_object_set_data( holder, "width_action", eact );
3103         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3104         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3105     }
3107     /* H */
3108     {
3109         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3110         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3111         eact = create_adjustment_action( "RectHeightAction",
3112                                          _("Height"), _("H:"), _("Height of rectangle"),
3113                                          "/tools/shapes/rect/height", 0,
3114                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3115                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3116                                          labels, values, G_N_ELEMENTS(labels),
3117                                          sp_rtb_height_value_changed );
3118         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3119         g_object_set_data( holder, "height_action", eact );
3120         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3121         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3122     }
3124     /* rx */
3125     {
3126         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3127         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3128         eact = create_adjustment_action( "RadiusXAction",
3129                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3130                                          "/tools/shapes/rect/rx", 0,
3131                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3132                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3133                                          labels, values, G_N_ELEMENTS(labels),
3134                                          sp_rtb_rx_value_changed);
3135         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3136         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3137     }
3139     /* ry */
3140     {
3141         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3142         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3143         eact = create_adjustment_action( "RadiusYAction",
3144                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3145                                          "/tools/shapes/rect/ry", 0,
3146                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3147                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3148                                          labels, values, G_N_ELEMENTS(labels),
3149                                          sp_rtb_ry_value_changed);
3150         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3151         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3152     }
3154     // add the units menu
3155     {
3156         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3157         gtk_action_group_add_action( mainActions, act );
3158     }
3160     /* Reset */
3161     {
3162         InkAction* inky = ink_action_new( "RectResetAction",
3163                                           _("Not rounded"),
3164                                           _("Make corners sharp"),
3165                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3166                                           secondarySize );
3167         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3168         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3169         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3170         g_object_set_data( holder, "not_rounded", inky );
3171     }
3173     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3174     sp_rtb_sensitivize( holder );
3176     sigc::connection *connection = new sigc::connection(
3177         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3178         );
3179     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3180     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3183 //########################
3184 //##       3D Box       ##
3185 //########################
3187 // normalize angle so that it lies in the interval [0,360]
3188 static double box3d_normalize_angle (double a) {
3189     double angle = a + ((int) (a/360.0))*360;
3190     if (angle < 0) {
3191         angle += 360.0;
3192     }
3193     return angle;
3196 static void
3197 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3198                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3199     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3200     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3201     //       are reset).
3202     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3204     if (is_infinite) {
3205         gtk_toggle_action_set_active(tact, TRUE);
3206         gtk_action_set_sensitive(act, TRUE);
3208         double angle = persp3d_get_infinite_angle(persp, axis);
3209         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3210             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3211         }
3212     } else {
3213         gtk_toggle_action_set_active(tact, FALSE);
3214         gtk_action_set_sensitive(act, FALSE);
3215     }
3218 static void
3219 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3220     if (!persp_repr) {
3221         g_print ("No perspective given to box3d_resync_toolbar().\n");
3222         return;
3223     }
3225     GtkWidget *tbl = GTK_WIDGET(data);
3226     GtkAdjustment *adj = 0;
3227     GtkAction *act = 0;
3228     GtkToggleAction *tact = 0;
3229     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3230     {
3231         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3232         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3233         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3235         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3236     }
3237     {
3238         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3239         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3240         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3242         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3243     }
3244     {
3245         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3246         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3247         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3249         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3250     }
3253 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3254                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3255                                                   bool /*is_interactive*/, gpointer data)
3257     GtkWidget *tbl = GTK_WIDGET(data);
3259     // quit if run by the attr_changed or selection changed listener
3260     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3261         return;
3262     }
3264     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3265     // sp_document_maybe_done() when the document is undo insensitive)
3266     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3268     // TODO: Only update the appropriate part of the toolbar
3269 //    if (!strcmp(name, "inkscape:vp_z")) {
3270         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3271 //    }
3273     Persp3D *persp = persp3d_get_from_repr(repr);
3274     persp3d_update_box_reprs(persp);
3276     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3279 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3281     NULL, /* child_added */
3282     NULL, /* child_removed */
3283     box3d_persp_tb_event_attr_changed,
3284     NULL, /* content_changed */
3285     NULL  /* order_changed */
3286 };
3288 /**
3289  *  \param selection Should not be NULL.
3290  */
3291 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3292 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3293 static void
3294 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3296     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3297     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3298     // update the perspectives with infinite VPs and leave the other ones untouched).
3300     Inkscape::XML::Node *persp_repr = NULL;
3301     purge_repr_listener(tbl, tbl);
3303     SPItem *item = selection->singleItem();
3304     if (item && SP_IS_BOX3D(item)) {
3305         // FIXME: Also deal with multiple selected boxes
3306         SPBox3D *box = SP_BOX3D(item);
3307         Persp3D *persp = box3d_get_perspective(box);
3308         persp_repr = SP_OBJECT_REPR(persp);
3309         if (persp_repr) {
3310             g_object_set_data(tbl, "repr", persp_repr);
3311             Inkscape::GC::anchor(persp_repr);
3312             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3313             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3314         }
3316         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3317         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3318         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3320         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3321         box3d_resync_toolbar(persp_repr, tbl);
3322         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3323     }
3326 static void
3327 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3329     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3330     SPDocument *document = sp_desktop_document(desktop);
3332     // quit if run by the attr_changed or selection changed listener
3333     if (g_object_get_data( dataKludge, "freeze" )) {
3334         return;
3335     }
3337     // in turn, prevent listener from responding
3338     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3340     //Persp3D *persp = document->current_persp3d;
3341     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3342     if (sel_persps.empty()) {
3343         // this can happen when the document is created; we silently ignore it
3344         return;
3345     }
3346     Persp3D *persp = sel_persps.front();
3348     persp->tmat.set_infinite_direction (axis, adj->value);
3349     SP_OBJECT(persp)->updateRepr();
3351     // TODO: use the correct axis here, too
3352     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3354     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3358 static void
3359 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3361     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3364 static void
3365 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3367     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3370 static void
3371 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3373     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3377 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3379     // TODO: Take all selected perspectives into account
3380     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3381     if (sel_persps.empty()) {
3382         // this can happen when the document is created; we silently ignore it
3383         return;
3384     }
3385     Persp3D *persp = sel_persps.front();
3387     bool set_infinite = gtk_toggle_action_get_active(act);
3388     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3391 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3393     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3396 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3398     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3401 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3403     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3406 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3408     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3409     EgeAdjustmentAction* eact = 0;
3410     SPDocument *document = sp_desktop_document (desktop);
3411     Persp3D *persp = document->current_persp3d;
3413     EgeAdjustmentAction* box3d_angle_x = 0;
3414     EgeAdjustmentAction* box3d_angle_y = 0;
3415     EgeAdjustmentAction* box3d_angle_z = 0;
3417     /* Angle X */
3418     {
3419         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3420         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3421         eact = create_adjustment_action( "3DBoxAngleXAction",
3422                                          _("Angle in X direction"), _("Angle X:"),
3423                                          // Translators: PL is short for 'perspective line'
3424                                          _("Angle of PLs in X direction"),
3425                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3426                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3427                                          -360.0, 360.0, 1.0, 10.0,
3428                                          labels, values, G_N_ELEMENTS(labels),
3429                                          box3d_angle_x_value_changed );
3430         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3431         g_object_set_data( holder, "box3d_angle_x_action", eact );
3432         box3d_angle_x = eact;
3433     }
3435     if (!persp3d_VP_is_finite(persp, Proj::X)) {
3436         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3437     } else {
3438         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3439     }
3442     /* VP X state */
3443     {
3444         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3445                                                       // Translators: VP is short for 'vanishing point'
3446                                                       _("State of VP in X direction"),
3447                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3448                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3449                                                       Inkscape::ICON_SIZE_DECORATION );
3450         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3451         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3452         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3453         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3454         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3455     }
3457     /* Angle Y */
3458     {
3459         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3460         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3461         eact = create_adjustment_action( "3DBoxAngleYAction",
3462                                          _("Angle in Y direction"), _("Angle Y:"),
3463                                          // Translators: PL is short for 'perspective line'
3464                                          _("Angle of PLs in Y direction"),
3465                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3466                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3467                                          -360.0, 360.0, 1.0, 10.0,
3468                                          labels, values, G_N_ELEMENTS(labels),
3469                                          box3d_angle_y_value_changed );
3470         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3471         g_object_set_data( holder, "box3d_angle_y_action", eact );
3472         box3d_angle_y = eact;
3473     }
3475     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3476         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3477     } else {
3478         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3479     }
3481     /* VP Y state */
3482     {
3483         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3484                                                       // Translators: VP is short for 'vanishing point'
3485                                                       _("State of VP in Y direction"),
3486                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3487                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3488                                                       Inkscape::ICON_SIZE_DECORATION );
3489         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3490         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3491         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3492         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3493         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3494     }
3496     /* Angle Z */
3497     {
3498         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3499         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3500         eact = create_adjustment_action( "3DBoxAngleZAction",
3501                                          _("Angle in Z direction"), _("Angle Z:"),
3502                                          // Translators: PL is short for 'perspective line'
3503                                          _("Angle of PLs in Z direction"),
3504                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3505                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3506                                          -360.0, 360.0, 1.0, 10.0,
3507                                          labels, values, G_N_ELEMENTS(labels),
3508                                          box3d_angle_z_value_changed );
3509         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3510         g_object_set_data( holder, "box3d_angle_z_action", eact );
3511         box3d_angle_z = eact;
3512     }
3514     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3515         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3516     } else {
3517         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3518     }
3520     /* VP Z state */
3521     {
3522         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3523                                                       // Translators: VP is short for 'vanishing point'
3524                                                       _("State of VP in Z direction"),
3525                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3526                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3527                                                       Inkscape::ICON_SIZE_DECORATION );
3528         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3529         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3530         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3531         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3532         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3533     }
3535     sigc::connection *connection = new sigc::connection(
3536         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3537        );
3538     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3539     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3542 //########################
3543 //##       Spiral       ##
3544 //########################
3546 static void
3547 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3549     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3551     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3552         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3553         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3554     }
3556     // quit if run by the attr_changed listener
3557     if (g_object_get_data( tbl, "freeze" )) {
3558         return;
3559     }
3561     // in turn, prevent listener from responding
3562     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3564     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3566     bool modmade = false;
3567     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3568          items != NULL;
3569          items = items->next)
3570     {
3571         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3572             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3573             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3574             SP_OBJECT((SPItem *) items->data)->updateRepr();
3575             modmade = true;
3576         }
3577     }
3579     g_free(namespaced_name);
3581     if (modmade) {
3582         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3583                                    _("Change spiral"));
3584     }
3586     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3589 static void
3590 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3592     sp_spl_tb_value_changed(adj, tbl, "revolution");
3595 static void
3596 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3598     sp_spl_tb_value_changed(adj, tbl, "expansion");
3601 static void
3602 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3604     sp_spl_tb_value_changed(adj, tbl, "t0");
3607 static void
3608 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3610     GtkWidget *tbl = GTK_WIDGET(obj);
3612     GtkAdjustment *adj;
3614     // fixme: make settable
3615     gdouble rev = 5;
3616     gdouble exp = 1.0;
3617     gdouble t0 = 0.0;
3619     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3620     gtk_adjustment_set_value(adj, rev);
3621     gtk_adjustment_value_changed(adj);
3623     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3624     gtk_adjustment_set_value(adj, exp);
3625     gtk_adjustment_value_changed(adj);
3627     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3628     gtk_adjustment_set_value(adj, t0);
3629     gtk_adjustment_value_changed(adj);
3631     spinbutton_defocus(GTK_OBJECT(tbl));
3635 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3636                                          gchar const */*old_value*/, gchar const */*new_value*/,
3637                                          bool /*is_interactive*/, gpointer data)
3639     GtkWidget *tbl = GTK_WIDGET(data);
3641     // quit if run by the _changed callbacks
3642     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3643         return;
3644     }
3646     // in turn, prevent callbacks from responding
3647     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3649     GtkAdjustment *adj;
3650     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3651     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3653     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3654     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3656     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3657     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3659     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3663 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3664     NULL, /* child_added */
3665     NULL, /* child_removed */
3666     spiral_tb_event_attr_changed,
3667     NULL, /* content_changed */
3668     NULL  /* order_changed */
3669 };
3671 static void
3672 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3674     int n_selected = 0;
3675     Inkscape::XML::Node *repr = NULL;
3677     purge_repr_listener( tbl, tbl );
3679     for (GSList const *items = selection->itemList();
3680          items != NULL;
3681          items = items->next)
3682     {
3683         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3684             n_selected++;
3685             repr = SP_OBJECT_REPR((SPItem *) items->data);
3686         }
3687     }
3689     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3691     if (n_selected == 0) {
3692         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3693     } else if (n_selected == 1) {
3694         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3696         if (repr) {
3697             g_object_set_data( tbl, "repr", repr );
3698             Inkscape::GC::anchor(repr);
3699             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3700             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3701         }
3702     } else {
3703         // FIXME: implement averaging of all parameters for multiple selected
3704         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3705         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3706     }
3710 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3712     EgeAdjustmentAction* eact = 0;
3713     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3715     {
3716         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3717         ege_output_action_set_use_markup( act, TRUE );
3718         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3719         g_object_set_data( holder, "mode_action", act );
3720     }
3722     /* Revolution */
3723     {
3724         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3725         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3726         eact = create_adjustment_action( "SpiralRevolutionAction",
3727                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3728                                          "/tools/shapes/spiral/revolution", 3.0,
3729                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3730                                          0.01, 1024.0, 0.1, 1.0,
3731                                          labels, values, G_N_ELEMENTS(labels),
3732                                          sp_spl_tb_revolution_value_changed, 1, 2);
3733         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3734     }
3736     /* Expansion */
3737     {
3738         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3739         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3740         eact = create_adjustment_action( "SpiralExpansionAction",
3741                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3742                                          "/tools/shapes/spiral/expansion", 1.0,
3743                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3744                                          0.0, 1000.0, 0.01, 1.0,
3745                                          labels, values, G_N_ELEMENTS(labels),
3746                                          sp_spl_tb_expansion_value_changed);
3747         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3748     }
3750     /* T0 */
3751     {
3752         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3753         gdouble values[] = {0, 0.5, 0.9};
3754         eact = create_adjustment_action( "SpiralT0Action",
3755                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3756                                          "/tools/shapes/spiral/t0", 0.0,
3757                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3758                                          0.0, 0.999, 0.01, 1.0,
3759                                          labels, values, G_N_ELEMENTS(labels),
3760                                          sp_spl_tb_t0_value_changed);
3761         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3762     }
3764     /* Reset */
3765     {
3766         InkAction* inky = ink_action_new( "SpiralResetAction",
3767                                           _("Defaults"),
3768                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3769                                           GTK_STOCK_CLEAR,
3770                                           secondarySize );
3771         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3772         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3773     }
3776     sigc::connection *connection = new sigc::connection(
3777         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3778         );
3779     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3780     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3783 //########################
3784 //##     Pen/Pencil     ##
3785 //########################
3787 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3788 static Glib::ustring const
3789 freehand_tool_name(GObject *dataKludge)
3791     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3792     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3793              ? "/tools/freehand/pen"
3794              : "/tools/freehand/pencil" );
3797 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3799     gint mode = ege_select_one_action_get_active(act);
3801     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3802     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3804     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3806     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3807     // preparatory work here
3808     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3809         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3810         sp_pen_context_set_polyline_mode(pc);
3811     }
3814 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3816     /* Freehand mode toggle buttons */
3817     {
3818         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3819         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3820         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3822         {
3823             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3825             GtkTreeIter iter;
3826             gtk_list_store_append( model, &iter );
3827             gtk_list_store_set( model, &iter,
3828                                 0, _("Bezier"),
3829                                 1, _("Create regular Bezier path"),
3830                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3831                                 -1 );
3833             gtk_list_store_append( model, &iter );
3834             gtk_list_store_set( model, &iter,
3835                                 0, _("Spiro"),
3836                                 1, _("Create Spiro path"),
3837                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3838                                 -1 );
3840             if (!tool_is_pencil) {
3841                 gtk_list_store_append( model, &iter );
3842                 gtk_list_store_set( model, &iter,
3843                                     0, _("Zigzag"),
3844                                     1, _("Create a sequence of straight line segments"),
3845                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3846                                     -1 );
3848                 gtk_list_store_append( model, &iter );
3849                 gtk_list_store_set( model, &iter,
3850                                     0, _("Paraxial"),
3851                                     1, _("Create a sequence of paraxial line segments"),
3852                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3853                                     -1 );
3854             }
3856             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3857                                                                 "FreehandModeActionPencil" :
3858                                                                 "FreehandModeActionPen",
3859                                                                 (_("Mode:")), (_("Mode of new lines drawn by this tool")), NULL, GTK_TREE_MODEL(model) );
3860             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3862             ege_select_one_action_set_appearance( act, "full" );
3863             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3864             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3865             ege_select_one_action_set_icon_column( act, 2 );
3866             ege_select_one_action_set_icon_size( act, secondarySize );
3867             ege_select_one_action_set_tooltip_column( act, 1  );
3869             ege_select_one_action_set_active( act, freehandMode);
3870             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3871         }
3872     }
3875 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3876     gint shape = ege_select_one_action_get_active( act );
3877     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3878     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3881 /**
3882  * \brief Generate the list of freehand advanced shape option entries.
3883  */
3884 GList * freehand_shape_dropdown_items_list() {
3885     GList *glist = NULL;
3887     glist = g_list_append (glist, _("None"));
3888     glist = g_list_append (glist, _("Triangle in"));
3889     glist = g_list_append (glist, _("Triangle out"));
3890     glist = g_list_append (glist, _("Ellipse"));
3891     glist = g_list_append (glist, _("From clipboard"));
3893     return glist;
3896 static void
3897 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3898     /*advanced shape options */
3899     {
3900         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3901         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3903         GList* items = 0;
3904         gint count = 0;
3905         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3906         {
3907             GtkTreeIter iter;
3908             gtk_list_store_append( model, &iter );
3909             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3910             count++;
3911         }
3912         g_list_free( items );
3913         items = 0;
3914         EgeSelectOneAction* act1 = ege_select_one_action_new(
3915             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3916             _("Shape:"), (_("Shape of new paths drawn by this tool")), NULL, GTK_TREE_MODEL(model));
3917         g_object_set( act1, "short_label", _("Shape:"), NULL );
3918         ege_select_one_action_set_appearance( act1, "compact" );
3919         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3920         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3921         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3922         g_object_set_data( holder, "shape_action", act1 );
3923     }
3926 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3928     sp_add_freehand_mode_toggle(mainActions, holder, false);
3929     freehand_add_advanced_shape_options(mainActions, holder, false);
3933 static void
3934 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3936     GtkWidget *tbl = GTK_WIDGET(obj);
3938     GtkAdjustment *adj;
3940     // fixme: make settable
3941     gdouble tolerance = 4;
3943     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3944     gtk_adjustment_set_value(adj, tolerance);
3945     gtk_adjustment_value_changed(adj);
3947     spinbutton_defocus(GTK_OBJECT(tbl));
3950 static void
3951 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3953     // quit if run by the attr_changed listener
3954     if (g_object_get_data( tbl, "freeze" )) {
3955         return;
3956     }
3957     // in turn, prevent listener from responding
3958     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3959     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3960     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3961     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3964 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3965 public:
3966     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3967     {
3968         g_object_set_data(_obj, "prefobserver", this);
3969     }
3970     virtual ~PencilToleranceObserver() {
3971         if (g_object_get_data(_obj, "prefobserver") == this) {
3972                 g_object_set_data(_obj, "prefobserver", NULL);
3973         }
3974     }
3975     virtual void notify(Inkscape::Preferences::Entry const &val) {
3976         GObject* tbl = _obj;
3977         if (g_object_get_data( tbl, "freeze" )) {
3978             return;
3979         }
3980         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3982         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3984         double v = val.getDouble(adj->value);
3985         gtk_adjustment_set_value(adj, v);
3986         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3987     }
3988 private:
3989     GObject *_obj;
3990 };
3993 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3995     sp_add_freehand_mode_toggle(mainActions, holder, true);
3997     EgeAdjustmentAction* eact = 0;
3999     /* Tolerance */
4000     {
4001         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
4002         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
4003         eact = create_adjustment_action( "PencilToleranceAction",
4004                                          _("Smoothing:"), _("Smoothing: "),
4005                  _("How much smoothing (simplifying) is applied to the line"),
4006                                          "/tools/freehand/pencil/tolerance",
4007                                          3.0,
4008                                          GTK_WIDGET(desktop->canvas), NULL,
4009                                          holder, TRUE, "altx-pencil",
4010                                          1, 100.0, 0.5, 1.0,
4011                                          labels, values, G_N_ELEMENTS(labels),
4012                                          sp_pencil_tb_tolerance_value_changed,
4013                                          1, 2);
4014         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4015         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4017         PencilToleranceObserver *obs =
4018             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4019     }
4021     /* advanced shape options */
4022     freehand_add_advanced_shape_options(mainActions, holder, true);
4024     /* Reset */
4025     {
4026         InkAction* inky = ink_action_new( "PencilResetAction",
4027                                           _("Defaults"),
4028                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4029                                           GTK_STOCK_CLEAR,
4030                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4031         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4032         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4033     }
4035     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4040 //########################
4041 //##       Tweak        ##
4042 //########################
4044 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4046     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4047     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4050 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4052     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4053     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4056 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4058     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4059     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4062 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4064     int mode = ege_select_one_action_get_active( act );
4065     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4066     prefs->setInt("/tools/tweak/mode", mode);
4068     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4069     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4070     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4071     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4072     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4073     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4074     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4075         if (doh) gtk_action_set_sensitive (doh, TRUE);
4076         if (dos) gtk_action_set_sensitive (dos, TRUE);
4077         if (dol) gtk_action_set_sensitive (dol, TRUE);
4078         if (doo) gtk_action_set_sensitive (doo, TRUE);
4079         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4080         if (fid) gtk_action_set_sensitive (fid, FALSE);
4081     } else {
4082         if (doh) gtk_action_set_sensitive (doh, FALSE);
4083         if (dos) gtk_action_set_sensitive (dos, FALSE);
4084         if (dol) gtk_action_set_sensitive (dol, FALSE);
4085         if (doo) gtk_action_set_sensitive (doo, FALSE);
4086         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4087         if (fid) gtk_action_set_sensitive (fid, TRUE);
4088     }
4091 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4093     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4094     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4097 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4098     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4099     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4101 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4102     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4103     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4105 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4106     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4107     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4109 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4110     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4111     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4114 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4116     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4117     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4119     {
4120         /* Width */
4121         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4122         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4123         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4124                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4125                                                               "/tools/tweak/width", 15,
4126                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4127                                                               1, 100, 1.0, 10.0,
4128                                                               labels, values, G_N_ELEMENTS(labels),
4129                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4130         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4131         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4132         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4133     }
4136     {
4137         /* Force */
4138         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4139         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4140         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4141                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4142                                                               "/tools/tweak/force", 20,
4143                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4144                                                               1, 100, 1.0, 10.0,
4145                                                               labels, values, G_N_ELEMENTS(labels),
4146                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4147         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4148         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4149         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4150     }
4152     /* Mode */
4153     {
4154         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4156         GtkTreeIter iter;
4157         gtk_list_store_append( model, &iter );
4158         gtk_list_store_set( model, &iter,
4159                             0, _("Move mode"),
4160                             1, _("Move objects in any direction"),
4161                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4162                             -1 );
4164         gtk_list_store_append( model, &iter );
4165         gtk_list_store_set( model, &iter,
4166                             0, _("Move in/out mode"),
4167                             1, _("Move objects towards cursor; with Shift from cursor"),
4168                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4169                             -1 );
4171         gtk_list_store_append( model, &iter );
4172         gtk_list_store_set( model, &iter,
4173                             0, _("Move jitter mode"),
4174                             1, _("Move objects in random directions"),
4175                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4176                             -1 );
4178         gtk_list_store_append( model, &iter );
4179         gtk_list_store_set( model, &iter,
4180                             0, _("Scale mode"),
4181                             1, _("Shrink objects, with Shift enlarge"),
4182                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4183                             -1 );
4185         gtk_list_store_append( model, &iter );
4186         gtk_list_store_set( model, &iter,
4187                             0, _("Rotate mode"),
4188                             1, _("Rotate objects, with Shift counterclockwise"),
4189                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4190                             -1 );
4192         gtk_list_store_append( model, &iter );
4193         gtk_list_store_set( model, &iter,
4194                             0, _("Duplicate/delete mode"),
4195                             1, _("Duplicate objects, with Shift delete"),
4196                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4197                             -1 );
4199         gtk_list_store_append( model, &iter );
4200         gtk_list_store_set( model, &iter,
4201                             0, _("Push mode"),
4202                             1, _("Push parts of paths in any direction"),
4203                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4204                             -1 );
4206         gtk_list_store_append( model, &iter );
4207         gtk_list_store_set( model, &iter,
4208                             0, _("Shrink/grow mode"),
4209                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4210                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4211                             -1 );
4213         gtk_list_store_append( model, &iter );
4214         gtk_list_store_set( model, &iter,
4215                             0, _("Attract/repel mode"),
4216                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4217                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4218                             -1 );
4220         gtk_list_store_append( model, &iter );
4221         gtk_list_store_set( model, &iter,
4222                             0, _("Roughen mode"),
4223                             1, _("Roughen parts of paths"),
4224                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4225                             -1 );
4227         gtk_list_store_append( model, &iter );
4228         gtk_list_store_set( model, &iter,
4229                             0, _("Color paint mode"),
4230                             1, _("Paint the tool's color upon selected objects"),
4231                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4232                             -1 );
4234         gtk_list_store_append( model, &iter );
4235         gtk_list_store_set( model, &iter,
4236                             0, _("Color jitter mode"),
4237                             1, _("Jitter the colors of selected objects"),
4238                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4239                             -1 );
4241         gtk_list_store_append( model, &iter );
4242         gtk_list_store_set( model, &iter,
4243                             0, _("Blur mode"),
4244                             1, _("Blur selected objects more; with Shift, blur less"),
4245                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4246                             -1 );
4249         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4250         g_object_set( act, "short_label", _("Mode:"), NULL );
4251         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4252         g_object_set_data( holder, "mode_action", act );
4254         ege_select_one_action_set_appearance( act, "full" );
4255         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4256         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4257         ege_select_one_action_set_icon_column( act, 2 );
4258         ege_select_one_action_set_icon_size( act, secondarySize );
4259         ege_select_one_action_set_tooltip_column( act, 1  );
4261         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4262         ege_select_one_action_set_active( act, mode );
4263         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4265         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4266     }
4268     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4270     {
4271         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4272         ege_output_action_set_use_markup( act, TRUE );
4273         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4274         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4275             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4276         g_object_set_data( holder, "tweak_channels_label", act);
4277     }
4279     {
4280         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4281                                                       _("Hue"),
4282                                                       _("In color mode, act on objects' hue"),
4283                                                       NULL,
4284                                                       Inkscape::ICON_SIZE_DECORATION );
4285         //TRANSLATORS:  "H" here stands for hue
4286         g_object_set( act, "short_label", _("H"), NULL );
4287         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4288         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4289         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4290         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4291             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4292         g_object_set_data( holder, "tweak_doh", act);
4293     }
4294     {
4295         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4296                                                       _("Saturation"),
4297                                                       _("In color mode, act on objects' saturation"),
4298                                                       NULL,
4299                                                       Inkscape::ICON_SIZE_DECORATION );
4300         //TRANSLATORS: "S" here stands for Saturation
4301         g_object_set( act, "short_label", _("S"), NULL );
4302         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4303         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4304         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4305         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4306             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4307         g_object_set_data( holder, "tweak_dos", act );
4308     }
4309     {
4310         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4311                                                       _("Lightness"),
4312                                                       _("In color mode, act on objects' lightness"),
4313                                                       NULL,
4314                                                       Inkscape::ICON_SIZE_DECORATION );
4315         //TRANSLATORS: "L" here stands for Lightness
4316         g_object_set( act, "short_label", _("L"), NULL );
4317         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4318         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4319         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4320         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4321             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4322         g_object_set_data( holder, "tweak_dol", act );
4323     }
4324     {
4325         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4326                                                       _("Opacity"),
4327                                                       _("In color mode, act on objects' opacity"),
4328                                                       NULL,
4329                                                       Inkscape::ICON_SIZE_DECORATION );
4330         //TRANSLATORS: "O" here stands for Opacity
4331         g_object_set( act, "short_label", _("O"), NULL );
4332         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4333         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4334         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4335         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4336             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4337         g_object_set_data( holder, "tweak_doo", act );
4338     }
4340     {   /* Fidelity */
4341         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4342         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4343         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4344                                                               _("Fidelity"), _("Fidelity:"),
4345                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4346                                                               "/tools/tweak/fidelity", 50,
4347                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4348                                                               1, 100, 1.0, 10.0,
4349                                                               labels, values, G_N_ELEMENTS(labels),
4350                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4351         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4352         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4353         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4354             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4355         g_object_set_data( holder, "tweak_fidelity", eact );
4356     }
4359     /* Use Pressure button */
4360     {
4361         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4362                                                       _("Pressure"),
4363                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4364                                                       INKSCAPE_ICON_DRAW_USE_PRESSURE,
4365                                                       Inkscape::ICON_SIZE_DECORATION );
4366         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4367         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4368         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4369     }
4374 //########################
4375 //##     Calligraphy    ##
4376 //########################
4377 static void update_presets_list (GObject *tbl)
4379     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4380     if (g_object_get_data(tbl, "presets_blocked"))
4381         return;
4383     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4384     if (!sel) {
4385         // WTF!? This will cause a segfault if ever reached
4386         //ege_select_one_action_set_active(sel, 0);
4387         return;
4388     }
4390     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4392     int ege_index = 1;
4393     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4394         bool match = true;
4396         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4397         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4398             Glib::ustring entry_name = j->getEntryName();
4399             if (entry_name == "id" || entry_name == "name") continue;
4401             void *widget = g_object_get_data(tbl, entry_name.data());
4402             if (widget) {
4403                 if (GTK_IS_ADJUSTMENT(widget)) {
4404                     double v = j->getDouble();
4405                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4406                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4407                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4408                         match = false;
4409                         break;
4410                     }
4411                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4412                     bool v = j->getBool();
4413                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4414                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4415                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4416                         match = false;
4417                         break;
4418                     }
4419                 }
4420             }
4421         }
4423         if (match) {
4424             // newly added item is at the same index as the
4425             // save command, so we need to change twice for it to take effect
4426             ege_select_one_action_set_active(sel, 0);
4427             ege_select_one_action_set_active(sel, ege_index); // one-based index
4428             return;
4429         }
4430     }
4432     // no match found
4433     ege_select_one_action_set_active(sel, 0);
4436 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4438     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4439     prefs->setDouble( "/tools/calligraphic/mass", adj->value );
4440     update_presets_list(tbl);
4443 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4445     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4446     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value );
4447     update_presets_list(tbl);
4450 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4452     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4453     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4454     update_presets_list(tbl);
4457 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4459     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4460     prefs->setDouble( "/tools/calligraphic/width", adj->value );
4461     update_presets_list(tbl);
4464 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4466     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4467     prefs->setDouble("/tools/calligraphic/thinning", adj->value );
4468     update_presets_list(tbl);
4471 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4473     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4474     prefs->setDouble( "/tools/calligraphic/flatness", adj->value );
4475     update_presets_list(tbl);
4478 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4480     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4481     prefs->setDouble( "/tools/calligraphic/tremor", adj->value );
4482     update_presets_list(tbl);
4485 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4487     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4488     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4489     update_presets_list(tbl);
4492 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4494     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4495     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4496     update_presets_list(tbl);
4499 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4501     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4502     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4503     update_presets_list(tbl);
4506 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4508     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4509     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4510     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4511     update_presets_list(tbl);
4512     if (calligraphy_angle )
4513         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4517 static gchar const *const widget_names[] = {
4518     "width",
4519     "mass",
4520     "wiggle",
4521     "angle",
4522     "thinning",
4523     "tremor",
4524     "flatness",
4525     "cap_rounding",
4526     "usepressure",
4527     "tracebackground",
4528     "usetilt"
4529 };
4532 static void sp_dcc_build_presets_list(GObject *tbl)
4534     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4536     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4537     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4538     gtk_list_store_clear (model);
4540     {
4541         GtkTreeIter iter;
4542         gtk_list_store_append( model, &iter );
4543         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4544     }
4546     // iterate over all presets to populate the list
4547     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4548     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4549     int ii=1;
4551     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4552         GtkTreeIter iter;
4553         Glib::ustring preset_name = prefs->getString(*i + "/name");
4554         gtk_list_store_append( model, &iter );
4555         gtk_list_store_set( model, &iter, 0, _(preset_name.data()), 1, ii++, -1 );
4556     }
4558     {
4559         GtkTreeIter iter;
4560         gtk_list_store_append( model, &iter );
4561         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4562         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4563     }
4565     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4567     update_presets_list (tbl);
4570 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4572     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4573     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4574     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4575     if (! desktop) return;
4577     if (g_object_get_data(tbl, "presets_blocked"))
4578         return;
4580     CalligraphicProfileRename::show(desktop);
4581     if ( !CalligraphicProfileRename::applied()) {
4582         // dialog cancelled
4583         update_presets_list (tbl);
4584         return;
4585     }
4586     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4588     if (profile_name.empty()) {
4589         // empty name entered
4590         update_presets_list (tbl);
4591         return;
4592     }
4594     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4596     // If there's a preset with the given name, find it and set save_path appropriately
4597     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4598     int total_presets = presets.size();
4599     int new_index = -1;
4600     Glib::ustring save_path; // profile pref path without a trailing slash
4602     int temp_index = 0;
4603     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4604         Glib::ustring name = prefs->getString(*i + "/name");
4605         if (!name.empty() && profile_name == name) {
4606             new_index = temp_index;
4607             save_path = *i;
4608             break;
4609         }
4610     }
4612     if (new_index == -1) {
4613         // no preset with this name, create
4614         new_index = total_presets + 1;
4615         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4616         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4617         g_free(profile_id);
4618     }
4620     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4621         gchar const *const widget_name = widget_names[i];
4622         void *widget = g_object_get_data(tbl, widget_name);
4623         if (widget) {
4624             if (GTK_IS_ADJUSTMENT(widget)) {
4625                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4626                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4627                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4628             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4629                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4630                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4631                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4632             } else {
4633                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4634             }
4635         } else {
4636             g_warning("Bad key when writing preset: %s\n", widget_name);
4637         }
4638     }
4639     prefs->setString(save_path + "/name", profile_name);
4641     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4642     sp_dcc_build_presets_list (tbl);
4646 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4648     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4650     gint preset_index = ege_select_one_action_get_active( act );
4651     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4652     // even when the preset is not changed. It would be good to replace it with something more
4653     // modern. Index 0 means "No preset", so we don't do anything.
4654     if (preset_index == 0) return;
4656     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4658     if (preset_index == save_presets_index) {
4659         // this is the Save command
4660         sp_dcc_save_profile(NULL, tbl);
4661         return;
4662     }
4664     if (g_object_get_data(tbl, "presets_blocked"))
4665         return;
4667     // preset_index is one-based so we subtract 1
4668     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4669     Glib::ustring preset_path = presets.at(preset_index - 1);
4671     if (!preset_path.empty()) {
4672         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
4674         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4676         // Shouldn't this be std::map?
4677         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4678             Glib::ustring entry_name = i->getEntryName();
4679             if (entry_name == "id" || entry_name == "name") continue;
4680             void *widget = g_object_get_data(tbl, entry_name.data());
4681             if (widget) {
4682                 if (GTK_IS_ADJUSTMENT(widget)) {
4683                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4684                     gtk_adjustment_set_value(adj, i->getDouble());
4685                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4686                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4687                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4688                     gtk_toggle_action_set_active(toggle, i->getBool());
4689                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4690                 } else {
4691                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4692                 }
4693             } else {
4694                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4695             }
4696         }
4697         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4698     }
4702 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4704     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4705     {
4706         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4708         EgeAdjustmentAction* calligraphy_angle = 0;
4710         {
4711         /* Width */
4712         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4713         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4714         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4715                                                               _("Pen Width"), _("Width:"),
4716                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4717                                                               "/tools/calligraphic/width", 15,
4718                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4719                                                               1, 100, 1.0, 10.0,
4720                                                               labels, values, G_N_ELEMENTS(labels),
4721                                                               sp_ddc_width_value_changed,  1, 0);
4722         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4723         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4724         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4725         }
4727         {
4728         /* Thinning */
4729             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4730             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4731         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4732                                                               _("Stroke Thinning"), _("Thinning:"),
4733                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4734                                                               "/tools/calligraphic/thinning", 10,
4735                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4736                                                               -100, 100, 1, 10.0,
4737                                                               labels, values, G_N_ELEMENTS(labels),
4738                                                               sp_ddc_velthin_value_changed, 1, 0);
4739         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4740         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4741         }
4743         {
4744         /* Angle */
4745         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4746         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4747         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4748                                                               _("Pen Angle"), _("Angle:"),
4749                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4750                                                               "/tools/calligraphic/angle", 30,
4751                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4752                                                               -90.0, 90.0, 1.0, 10.0,
4753                                                               labels, values, G_N_ELEMENTS(labels),
4754                                                               sp_ddc_angle_value_changed, 1, 0 );
4755         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4756         g_object_set_data( holder, "angle_action", eact );
4757         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4758         calligraphy_angle = eact;
4759         }
4761         {
4762         /* Fixation */
4763             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4764         gdouble values[] = {0, 20, 40, 60, 90, 100};
4765         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4766                                                               _("Fixation"), _("Fixation:"),
4767                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4768                                                               "/tools/calligraphic/flatness", 90,
4769                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4770                                                               0.0, 100, 1.0, 10.0,
4771                                                               labels, values, G_N_ELEMENTS(labels),
4772                                                               sp_ddc_flatness_value_changed, 1, 0);
4773         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4774         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4775         }
4777         {
4778         /* Cap Rounding */
4779             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4780         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4781         // TRANSLATORS: "cap" means "end" (both start and finish) here
4782         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4783                                                               _("Cap rounding"), _("Caps:"),
4784                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4785                                                               "/tools/calligraphic/cap_rounding", 0.0,
4786                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4787                                                               0.0, 5.0, 0.01, 0.1,
4788                                                               labels, values, G_N_ELEMENTS(labels),
4789                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4790         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4791         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4792         }
4794         {
4795         /* Tremor */
4796             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4797         gdouble values[] = {0, 10, 20, 40, 60, 100};
4798         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4799                                                               _("Stroke Tremor"), _("Tremor:"),
4800                                                               _("Increase to make strokes rugged and trembling"),
4801                                                               "/tools/calligraphic/tremor", 0.0,
4802                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4803                                                               0.0, 100, 1, 10.0,
4804                                                               labels, values, G_N_ELEMENTS(labels),
4805                                                               sp_ddc_tremor_value_changed, 1, 0);
4807         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4808         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4809         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4810         }
4812         {
4813         /* Wiggle */
4814         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4815         gdouble values[] = {0, 20, 40, 60, 100};
4816         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4817                                                               _("Pen Wiggle"), _("Wiggle:"),
4818                                                               _("Increase to make the pen waver and wiggle"),
4819                                                               "/tools/calligraphic/wiggle", 0.0,
4820                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4821                                                               0.0, 100, 1, 10.0,
4822                                                               labels, values, G_N_ELEMENTS(labels),
4823                                                               sp_ddc_wiggle_value_changed, 1, 0);
4824         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4825         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4826         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4827         }
4829         {
4830         /* Mass */
4831             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4832         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4833         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4834                                                               _("Pen Mass"), _("Mass:"),
4835                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4836                                                               "/tools/calligraphic/mass", 2.0,
4837                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4838                                                               0.0, 100, 1, 10.0,
4839                                                               labels, values, G_N_ELEMENTS(labels),
4840                                                               sp_ddc_mass_value_changed, 1, 0);
4841         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4842         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4843         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4844         }
4847         /* Trace Background button */
4848         {
4849             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4850                                                           _("Trace Background"),
4851                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4852                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4853                                                           Inkscape::ICON_SIZE_DECORATION );
4854             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4855             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4856             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4857             g_object_set_data( holder, "tracebackground", act );
4858         }
4860         /* Use Pressure button */
4861         {
4862             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4863                                                           _("Pressure"),
4864                                                           _("Use the pressure of the input device to alter the width of the pen"),
4865                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
4866                                                           Inkscape::ICON_SIZE_DECORATION );
4867             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4868             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4869             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4870             g_object_set_data( holder, "usepressure", act );
4871         }
4873         /* Use Tilt button */
4874         {
4875             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4876                                                           _("Tilt"),
4877                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4878                                                           INKSCAPE_ICON_DRAW_USE_TILT,
4879                                                           Inkscape::ICON_SIZE_DECORATION );
4880             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4881             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4882             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4883             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4884             g_object_set_data( holder, "usetilt", act );
4885         }
4887         /*calligraphic profile */
4888         {
4889             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4890             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4891             ege_select_one_action_set_appearance (act1, "compact");
4892             g_object_set_data (holder, "profile_selector", act1 );
4894             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4896             sp_dcc_build_presets_list (holder);
4898             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4899             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4900         }
4901     }
4905 //########################
4906 //##    Circle / Arc    ##
4907 //########################
4909 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4911     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4912     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4914     if (v1 == 0 && v2 == 0) {
4915         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4916             gtk_action_set_sensitive( ocb, FALSE );
4917             gtk_action_set_sensitive( make_whole, FALSE );
4918         }
4919     } else {
4920         gtk_action_set_sensitive( ocb, TRUE );
4921         gtk_action_set_sensitive( make_whole, TRUE );
4922     }
4925 static void
4926 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4928     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4930     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4931         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4932         prefs->setDouble(Glib::ustring("/tools/shapes/arc/") + value_name, adj->value);
4933     }
4935     // quit if run by the attr_changed listener
4936     if (g_object_get_data( tbl, "freeze" )) {
4937         return;
4938     }
4940     // in turn, prevent listener from responding
4941     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4943     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4945     bool modmade = false;
4946     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4947          items != NULL;
4948          items = items->next)
4949     {
4950         SPItem *item = SP_ITEM(items->data);
4952         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4954             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4955             SPArc *arc = SP_ARC(item);
4957             if (!strcmp(value_name, "start"))
4958                 ge->start = (adj->value * M_PI)/ 180;
4959             else
4960                 ge->end = (adj->value * M_PI)/ 180;
4962             sp_genericellipse_normalize(ge);
4963             ((SPObject *)arc)->updateRepr();
4964             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4966             modmade = true;
4967         }
4968     }
4970     g_free(namespaced_name);
4972     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4974     sp_arctb_sensitivize( tbl, adj->value, other->value );
4976     if (modmade) {
4977         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4978                                    _("Arc: Change start/end"));
4979     }
4981     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4985 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4987     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4990 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4992     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4996 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4998     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4999     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5000         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5001         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
5002     }
5004     // quit if run by the attr_changed listener
5005     if (g_object_get_data( tbl, "freeze" )) {
5006         return;
5007     }
5009     // in turn, prevent listener from responding
5010     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5012     bool modmade = false;
5014     if ( ege_select_one_action_get_active(act) != 0 ) {
5015         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5016              items != NULL;
5017              items = items->next)
5018         {
5019             if (SP_IS_ARC((SPItem *) items->data)) {
5020                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5021                 repr->setAttribute("sodipodi:open", "true");
5022                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5023                 modmade = true;
5024             }
5025         }
5026     } else {
5027         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5028              items != NULL;
5029              items = items->next)
5030         {
5031             if (SP_IS_ARC((SPItem *) items->data))    {
5032                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5033                 repr->setAttribute("sodipodi:open", NULL);
5034                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5035                 modmade = true;
5036             }
5037         }
5038     }
5040     if (modmade) {
5041         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5042                                    _("Arc: Change open/closed"));
5043     }
5045     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5048 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5050     GtkAdjustment *adj;
5051     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5052     gtk_adjustment_set_value(adj, 0.0);
5053     gtk_adjustment_value_changed(adj);
5055     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5056     gtk_adjustment_set_value(adj, 0.0);
5057     gtk_adjustment_value_changed(adj);
5059     spinbutton_defocus( GTK_OBJECT(obj) );
5062 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5063                                       gchar const */*old_value*/, gchar const */*new_value*/,
5064                                       bool /*is_interactive*/, gpointer data)
5066     GObject *tbl = G_OBJECT(data);
5068     // quit if run by the _changed callbacks
5069     if (g_object_get_data( tbl, "freeze" )) {
5070         return;
5071     }
5073     // in turn, prevent callbacks from responding
5074     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5076     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5077     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5079     GtkAdjustment *adj1,*adj2;
5080     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5081     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5082     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5083     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5085     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5087     char const *openstr = NULL;
5088     openstr = repr->attribute("sodipodi:open");
5089     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5091     if (openstr) {
5092         ege_select_one_action_set_active( ocb, 1 );
5093     } else {
5094         ege_select_one_action_set_active( ocb, 0 );
5095     }
5097     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5100 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5101     NULL, /* child_added */
5102     NULL, /* child_removed */
5103     arc_tb_event_attr_changed,
5104     NULL, /* content_changed */
5105     NULL  /* order_changed */
5106 };
5109 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5111     int n_selected = 0;
5112     Inkscape::XML::Node *repr = NULL;
5114     purge_repr_listener( tbl, tbl );
5116     for (GSList const *items = selection->itemList();
5117          items != NULL;
5118          items = items->next)
5119     {
5120         if (SP_IS_ARC((SPItem *) items->data)) {
5121             n_selected++;
5122             repr = SP_OBJECT_REPR((SPItem *) items->data);
5123         }
5124     }
5126     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5128     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5129     if (n_selected == 0) {
5130         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5131     } else if (n_selected == 1) {
5132         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5133         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5135         if (repr) {
5136             g_object_set_data( tbl, "repr", repr );
5137             Inkscape::GC::anchor(repr);
5138             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5139             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5140         }
5141     } else {
5142         // FIXME: implement averaging of all parameters for multiple selected
5143         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5144         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5145         sp_arctb_sensitivize( tbl, 1, 0 );
5146     }
5150 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5152     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5154     EgeAdjustmentAction* eact = 0;
5155     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5158     {
5159         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5160         ege_output_action_set_use_markup( act, TRUE );
5161         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5162         g_object_set_data( holder, "mode_action", act );
5163     }
5165     /* Start */
5166     {
5167         eact = create_adjustment_action( "ArcStartAction",
5168                                          _("Start"), _("Start:"),
5169                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5170                                          "/tools/shapes/arc/start", 0.0,
5171                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5172                                          -360.0, 360.0, 1.0, 10.0,
5173                                          0, 0, 0,
5174                                          sp_arctb_start_value_changed);
5175         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5176     }
5178     /* End */
5179     {
5180         eact = create_adjustment_action( "ArcEndAction",
5181                                          _("End"), _("End:"),
5182                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5183                                          "/tools/shapes/arc/end", 0.0,
5184                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5185                                          -360.0, 360.0, 1.0, 10.0,
5186                                          0, 0, 0,
5187                                          sp_arctb_end_value_changed);
5188         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5189     }
5191     /* Segments / Pie checkbox */
5192     {
5193         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5195         GtkTreeIter iter;
5196         gtk_list_store_append( model, &iter );
5197         gtk_list_store_set( model, &iter,
5198                             0, _("Closed arc"),
5199                             1, _("Switch to segment (closed shape with two radii)"),
5200                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5201                             -1 );
5203         gtk_list_store_append( model, &iter );
5204         gtk_list_store_set( model, &iter,
5205                             0, _("Open Arc"),
5206                             1, _("Switch to arc (unclosed shape)"),
5207                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5208                             -1 );
5210         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5211         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5212         g_object_set_data( holder, "open_action", act );
5214         ege_select_one_action_set_appearance( act, "full" );
5215         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5216         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5217         ege_select_one_action_set_icon_column( act, 2 );
5218         ege_select_one_action_set_icon_size( act, secondarySize );
5219         ege_select_one_action_set_tooltip_column( act, 1  );
5221         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5222         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5223         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5224     }
5226     /* Make Whole */
5227     {
5228         InkAction* inky = ink_action_new( "ArcResetAction",
5229                                           _("Make whole"),
5230                                           _("Make the shape a whole ellipse, not arc or segment"),
5231                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5232                                           secondarySize );
5233         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5234         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5235         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5236         g_object_set_data( holder, "make_whole", inky );
5237     }
5239     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5240     // sensitivize make whole and open checkbox
5241     {
5242         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5243         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5244         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5245     }
5248     sigc::connection *connection = new sigc::connection(
5249         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5250         );
5251     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5252     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5258 // toggle button callbacks and updaters
5260 //########################
5261 //##      Dropper       ##
5262 //########################
5264 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5265     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5266     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5267     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5268     if ( set_action ) {
5269         if ( gtk_toggle_action_get_active( act ) ) {
5270             gtk_action_set_sensitive( set_action, TRUE );
5271         } else {
5272             gtk_action_set_sensitive( set_action, FALSE );
5273         }
5274     }
5276     spinbutton_defocus(GTK_OBJECT(tbl));
5279 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5280     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5281     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5282     spinbutton_defocus(GTK_OBJECT(tbl));
5286 /**
5287  * Dropper auxiliary toolbar construction and setup.
5288  *
5289  * TODO: Would like to add swatch of current color.
5290  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5291  *       can drag and drop places. Will provide a nice mixing palette.
5292  */
5293 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5295     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5296     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5298     {
5299         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5300         ege_output_action_set_use_markup( act, TRUE );
5301         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5302     }
5304     {
5305         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5306                                                       _("Pick opacity"),
5307                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5308                                                       NULL,
5309                                                       Inkscape::ICON_SIZE_DECORATION );
5310         g_object_set( act, "short_label", _("Pick"), NULL );
5311         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5312         g_object_set_data( holder, "pick_action", act );
5313         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5314         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5315     }
5317     {
5318         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5319                                                       _("Assign opacity"),
5320                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5321                                                       NULL,
5322                                                       Inkscape::ICON_SIZE_DECORATION );
5323         g_object_set( act, "short_label", _("Assign"), NULL );
5324         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5325         g_object_set_data( holder, "set_action", act );
5326         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5327         // make sure it's disabled if we're not picking alpha
5328         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5329         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5330     }
5334 //########################
5335 //##      LPETool       ##
5336 //########################
5338 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5340 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5341 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5343     using namespace Inkscape::LivePathEffect;
5345     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5346     SPEventContext *ec = desktop->event_context;
5347     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5348         return;
5349     }
5351     // only take action if run by the attr_changed listener
5352     if (!g_object_get_data(tbl, "freeze")) {
5353         // in turn, prevent listener from responding
5354         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5356         gint mode = ege_select_one_action_get_active(act);
5357         EffectType type = lpesubtools[mode].type;
5359         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5360         bool success = lpetool_try_construction(lc, type);
5361         if (success) {
5362             // since the construction was already performed, we set the state back to inactive
5363             ege_select_one_action_set_active(act, 0);
5364             mode = 0;
5365         } else {
5366             // switch to the chosen subtool
5367             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5368         }
5370         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5371             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5372             prefs->setInt( "/tools/lpetool/mode", mode );
5373         }
5375         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5376     }
5379 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5381     SPEventContext *ec = selection->desktop()->event_context;
5382     if (!SP_IS_LPETOOL_CONTEXT(ec))
5383         return;
5385     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5388 void
5389 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5391     using namespace Inkscape::LivePathEffect;
5392     SPEventContext *ec = selection->desktop()->event_context;
5393     if (!SP_IS_LPETOOL_CONTEXT(ec))
5394         return;
5395     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5397     lpetool_delete_measuring_items(lc);
5398     lpetool_create_measuring_items(lc, selection);
5400     // activate line segment combo box if a single item with LPELineSegment is selected
5401     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5402     SPItem *item = selection->singleItem();
5403     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5404         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5405         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5406         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5407             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5408             g_object_set_data(tbl, "currentlpe", lpe);
5409             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5410             gtk_action_set_sensitive(w, TRUE);
5411             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5412         } else {
5413             g_object_set_data(tbl, "currentlpe", NULL);
5414             g_object_set_data(tbl, "currentlpeitem", NULL);
5415             gtk_action_set_sensitive(w, FALSE);
5416         }
5417     } else {
5418         g_object_set_data(tbl, "currentlpe", NULL);
5419         g_object_set_data(tbl, "currentlpeitem", NULL);
5420         gtk_action_set_sensitive(w, FALSE);
5421     }
5424 static void
5425 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5426     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5427     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5429     bool show = gtk_toggle_action_get_active( act );
5430     prefs->setBool("/tools/lpetool/show_bbox",  show);
5432     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5433         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5434         lpetool_context_reset_limiting_bbox(lc);
5435     }
5438 static void
5439 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5440     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5441     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5442         return;
5444     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5445     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5446     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5447         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5448         bool show = gtk_toggle_action_get_active( act );
5449         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5450         lpetool_show_measuring_info(lc, show);
5451         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5452     }
5455 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5456     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5457     SPUnit const *unit = tracker->getActiveUnit();
5458     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5459     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5461     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5462     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5463         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5464         lpetool_delete_measuring_items(lc);
5465         lpetool_create_measuring_items(lc);
5466     }
5469 static void
5470 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5471     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5472     Inkscape::Selection *selection = desktop->selection;
5474     Geom::OptRect bbox = selection->bounds();
5476     if (bbox) {
5477         Geom::Point A(bbox->min());
5478         Geom::Point B(bbox->max());
5480         A *= desktop->doc2dt();
5481         B *= desktop->doc2dt();
5483         // TODO: should we provide a way to store points in prefs?
5484         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5485         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5486         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5487         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5488         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5490         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5491     }
5493     gtk_toggle_action_set_active(act, false);
5496 static void
5497 sp_line_segment_build_list(GObject *tbl)
5499     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5501     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5502     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5503     gtk_list_store_clear (model);
5505     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5506     {
5507         GtkTreeIter iter;
5508         gtk_list_store_append( model, &iter );
5509         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5510         gtk_list_store_append( model, &iter );
5511         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5512         gtk_list_store_append( model, &iter );
5513         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5514         gtk_list_store_append( model, &iter );
5515         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5516     }
5518     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5521 static void
5522 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5523     using namespace Inkscape::LivePathEffect;
5525     // quit if run by the attr_changed listener
5526     if (g_object_get_data(tbl, "freeze")) {
5527         return;
5528     }
5530     // in turn, prevent listener from responding
5531     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5533     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5534     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5535     if (lpeitem) {
5536         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5537         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5538         sp_lpe_item_update_patheffect(lpeitem, true, true);
5539     }
5541     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5544 static void
5545 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5546     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5548     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5549         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5550     }
5551     gtk_toggle_action_set_active(act, false);
5554 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5556     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5557     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5558     g_object_set_data(holder, "tracker", tracker);
5559     SPUnit const *unit = tracker->getActiveUnit();
5561     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5562     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5564     /** Automatically create a list of LPEs that get added to the toolbar **/
5565     {
5566         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5568         GtkTreeIter iter;
5570         // the first toggle button represents the state that no subtool is active (remove this when
5571         // this can be modeled by EgeSelectOneAction or some other action)
5572         gtk_list_store_append( model, &iter );
5573         gtk_list_store_set( model, &iter,
5574                             0, _("All inactive"),
5575                             1, _("No geometric tool is active"),
5576                             2, "draw-geometry-inactive",
5577                             -1 );
5579         Inkscape::LivePathEffect::EffectType type;
5580         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5581             type =  lpesubtools[i].type;
5582             gtk_list_store_append( model, &iter );
5583             gtk_list_store_set( model, &iter,
5584                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5585                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5586                                 2, lpesubtools[i].icon_name,
5587                                 -1 );
5588         }
5590         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5591         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5592         g_object_set_data( holder, "lpetool_mode_action", act );
5594         ege_select_one_action_set_appearance( act, "full" );
5595         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5596         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5597         ege_select_one_action_set_icon_column( act, 2 );
5598         ege_select_one_action_set_tooltip_column( act, 1  );
5600         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5601         ege_select_one_action_set_active( act, lpeToolMode );
5602         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5603     }
5605     /* Show limiting bounding box */
5606     {
5607         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5608                                                       _("Show limiting bounding box"),
5609                                                       _("Show bounding box (used to cut infinite lines)"),
5610                                                       "show-bounding-box",
5611                                                       Inkscape::ICON_SIZE_DECORATION );
5612         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5613         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5614         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5615     }
5617     /* Set limiting bounding box to bbox of current selection */
5618     {
5619         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5620                                                       _("Get limiting bounding box from selection"),
5621                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5622                                                       "draw-geometry-set-bounding-box",
5623                                                       Inkscape::ICON_SIZE_DECORATION );
5624         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5625         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5626         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5627     }
5630     /* Combo box to choose line segment type */
5631     {
5632         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5633         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5634         ege_select_one_action_set_appearance (act, "compact");
5635         g_object_set_data (holder, "lpetool_line_segment_action", act );
5637         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5639         sp_line_segment_build_list (holder);
5641         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5642         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5643         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5644     }
5646     /* Display measuring info for selected items */
5647     {
5648         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5649                                                       _("Display measuring info"),
5650                                                       _("Display measuring info for selected items"),
5651                                                       "draw-geometry-show-measuring-info",
5652                                                       Inkscape::ICON_SIZE_DECORATION );
5653         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5654         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5655         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5656     }
5658     // add the units menu
5659     {
5660         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5661         gtk_action_group_add_action( mainActions, act );
5662         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5663         g_object_set_data(holder, "lpetool_units_action", act);
5664         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5665     }
5667     /* Open LPE dialog (to adapt parameters numerically) */
5668     {
5669         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5670                                                       _("Open LPE dialog"),
5671                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5672                                                       "dialog-geometry",
5673                                                       Inkscape::ICON_SIZE_DECORATION );
5674         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5675         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5676         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5677     }
5679     //watch selection
5680     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5682     sigc::connection *c_selection_modified =
5683         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5684                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5685     pool->add_connection ("selection-modified", c_selection_modified);
5687     sigc::connection *c_selection_changed =
5688         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5689                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5690     pool->add_connection ("selection-changed", c_selection_changed);
5693 //########################
5694 //##       Eraser       ##
5695 //########################
5697 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5699     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5700     prefs->setDouble( "/tools/eraser/width", adj->value );
5701     update_presets_list(tbl);
5704 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5706     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5707     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5708     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5709         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5710         prefs->setBool( "/tools/eraser/mode", eraserMode );
5711     }
5713     // only take action if run by the attr_changed listener
5714     if (!g_object_get_data( tbl, "freeze" )) {
5715         // in turn, prevent listener from responding
5716         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5718         if ( eraserMode != 0 ) {
5719         } else {
5720         }
5721         // TODO finish implementation
5723         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5724     }
5727 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5729     {
5730         /* Width */
5731         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5732         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5733         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5734                                                               _("Pen Width"), _("Width:"),
5735                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5736                                                               "/tools/eraser/width", 15,
5737                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5738                                                               1, 100, 1.0, 10.0,
5739                                                               labels, values, G_N_ELEMENTS(labels),
5740                                                               sp_erc_width_value_changed, 1, 0);
5741         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5742         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5743         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5744     }
5746     {
5747         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5749         GtkTreeIter iter;
5750         gtk_list_store_append( model, &iter );
5751         gtk_list_store_set( model, &iter,
5752                             0, _("Delete"),
5753                             1, _("Delete objects touched by the eraser"),
5754                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5755                             -1 );
5757         gtk_list_store_append( model, &iter );
5758         gtk_list_store_set( model, &iter,
5759                             0, _("Cut"),
5760                             1, _("Cut out from objects"),
5761                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5762                             -1 );
5764         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5765         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5766         g_object_set_data( holder, "eraser_mode_action", act );
5768         ege_select_one_action_set_appearance( act, "full" );
5769         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5770         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5771         ege_select_one_action_set_icon_column( act, 2 );
5772         ege_select_one_action_set_tooltip_column( act, 1  );
5774         /// @todo Convert to boolean?
5775         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5776         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5777         ege_select_one_action_set_active( act, eraserMode );
5778         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5779     }
5783 //########################
5784 //##    Text Toolbox    ##
5785 //########################
5786 /*
5787 static void
5788 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5790     //Call back for letter sizing spinbutton
5793 static void
5794 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5796     //Call back for line height spinbutton
5799 static void
5800 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5802     //Call back for horizontal kerning spinbutton
5805 static void
5806 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5808     //Call back for vertical kerning spinbutton
5811 static void
5812 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5814     //Call back for letter rotation spinbutton
5815 }*/
5817 namespace {
5819 void
5820 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5822     // quit if run by the _changed callbacks
5823     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
5824         return;
5825     }
5827     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5829     SPStyle *query =
5830         sp_style_new (SP_ACTIVE_DOCUMENT);
5832     int result_family =
5833         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5835     int result_style =
5836         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5838     int result_numbers =
5839         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5841     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5843     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5844     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5845         // there are no texts in selection, read from prefs
5847             sp_style_read_from_prefs(query, "/tools/text");
5849             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5850             // do not reset the toolbar style from prefs if we already did it last time
5851             sp_style_unref(query);
5852             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5853             return;
5854         }
5855         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5856     } else {
5857         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5858     }
5860     if (query->text)
5861     {
5862         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5863             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5864             gtk_entry_set_text (GTK_ENTRY (entry), "");
5866         } else if (query->text->font_specification.value || query->text->font_family.value) {
5868             Gtk::ComboBoxEntry *combo = (Gtk::ComboBoxEntry *) (g_object_get_data (G_OBJECT (tbl), "family-entry-combo"));
5869             GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5871             // Get the font that corresponds
5872             Glib::ustring familyName;
5874             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5875             if (font) {
5876                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5877                 font->Unref();
5878                 font = NULL;
5879             }
5881             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5883             Gtk::TreeIter iter;
5884             try {
5885                 Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5886                 Glib::RefPtr<Gtk::TreeModel> model = combo->get_model();
5887                 iter = model->get_iter(path);
5888             } catch (...) {
5889                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5890                 sp_style_unref(query);
5891                 g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5892                 return;
5893             }
5895             combo->set_active (iter);
5896         }
5898         //Size
5899         {
5900             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5901             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5902             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5903             g_free(str);
5904         }
5906         //Anchor
5907         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5908         {
5909             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5910             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5911             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5912             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5913         }
5914         else
5915         {
5916             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5917             {
5918                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5919                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5920                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5921                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5922             }
5923             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5924             {
5925                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5926                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5927                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5928                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5929             }
5930             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5931             {
5932                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5933                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5934                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5935                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5936             }
5937         }
5939         //Style
5940         {
5941             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5943             gboolean active = gtk_toggle_button_get_active (button);
5944             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));
5946             if (active != check)
5947             {
5948                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5949                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5950                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5951             }
5952         }
5954         {
5955             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5957             gboolean active = gtk_toggle_button_get_active (button);
5958             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5960             if (active != check)
5961             {
5962                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5963                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5964                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5965             }
5966         }
5968         //Orientation
5969         //locking both buttons, changing one affect all group (both)
5970         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5971         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5973         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5974         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5976         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5977         {
5978             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5979         }
5980         else
5981         {
5982             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5983         }
5984         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5985         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5986     }
5988     sp_style_unref(query);
5990     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5993 void
5994 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5996     sp_text_toolbox_selection_changed (selection, tbl);
5999 void
6000 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
6002     sp_text_toolbox_selection_changed (NULL, tbl);
6005 void
6006 sp_text_toolbox_family_changed (GtkComboBoxEntry    *,
6007                                 GObject             *tbl)
6009     // quit if run by the _changed callbacks
6010     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6011         return;
6012     }
6014     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6016     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6017     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6018     const gchar* family = gtk_entry_get_text (GTK_ENTRY (entry));
6020     //g_print ("family changed to: %s\n", family);
6022     SPStyle *query =
6023         sp_style_new (SP_ACTIVE_DOCUMENT);
6025     int result_fontspec =
6026         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6028     SPCSSAttr *css = sp_repr_css_attr_new ();
6030     // First try to get the font spec from the stored value
6031     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6033     if (fontSpec.empty()) {
6034         // Construct a new font specification if it does not yet exist
6035         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6036         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6037         fontFromStyle->Unref();
6038     }
6040     if (!fontSpec.empty()) {
6042         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6044         if (!newFontSpec.empty()) {
6046             if (fontSpec != newFontSpec) {
6048                 font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6050                 if (font) {
6051                     sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6053                     // Set all the these just in case they were altered when finding the best
6054                     // match for the new family and old style...
6056                     gchar c[256];
6058                     font->Family(c, 256);
6060                     sp_repr_css_set_property (css, "font-family", c);
6062                     font->Attribute( "weight", c, 256);
6063                     sp_repr_css_set_property (css, "font-weight", c);
6065                     font->Attribute("style", c, 256);
6066                     sp_repr_css_set_property (css, "font-style", c);
6068                     font->Attribute("stretch", c, 256);
6069                     sp_repr_css_set_property (css, "font-stretch", c);
6071                     font->Attribute("variant", c, 256);
6072                     sp_repr_css_set_property (css, "font-variant", c);
6074                     font->Unref();
6075                 }
6076             }
6078         } else {
6079             // If the old font on selection (or default) was not existing on the system,
6080             // ReplaceFontSpecificationFamily does not work. In that case we fall back to blindly
6081             // setting the family reported by the family chooser.
6083             //g_print ("fallback setting family: %s\n", family);
6084             sp_repr_css_set_property (css, "-inkscape-font-specification", family);
6085             sp_repr_css_set_property (css, "font-family", family);
6086         }
6087     }
6089     // If querying returned nothing, set the default style of the tool (for new texts)
6090     if (result_fontspec == QUERY_STYLE_NOTHING)
6091     {
6092         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6093         prefs->mergeStyle("/tools/text/style", css);
6094         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6095     }
6096     else
6097     {
6098         sp_desktop_set_style (desktop, css, true, true);
6099     }
6101     sp_style_unref(query);
6103     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6104                                    _("Text: Change font family"));
6105     sp_repr_css_attr_unref (css);
6107     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6109     // unfreeze
6110     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6112     // focus to canvas
6113     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6117 void
6118 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6119                                    gpointer          data)
6121     if (g_object_get_data (G_OBJECT (button), "block")) return;
6122     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6123     int prop = GPOINTER_TO_INT(data);
6125     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6126     SPCSSAttr *css = sp_repr_css_attr_new ();
6128     switch (prop)
6129     {
6130         case 0:
6131         {
6132             sp_repr_css_set_property (css, "text-anchor", "start");
6133             sp_repr_css_set_property (css, "text-align", "start");
6134             break;
6135         }
6136         case 1:
6137         {
6138             sp_repr_css_set_property (css, "text-anchor", "middle");
6139             sp_repr_css_set_property (css, "text-align", "center");
6140             break;
6141         }
6143         case 2:
6144         {
6145             sp_repr_css_set_property (css, "text-anchor", "end");
6146             sp_repr_css_set_property (css, "text-align", "end");
6147             break;
6148         }
6150         case 3:
6151         {
6152             sp_repr_css_set_property (css, "text-anchor", "start");
6153             sp_repr_css_set_property (css, "text-align", "justify");
6154             break;
6155         }
6156     }
6158     SPStyle *query =
6159         sp_style_new (SP_ACTIVE_DOCUMENT);
6160     int result_numbers =
6161         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6163     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6164     if (result_numbers == QUERY_STYLE_NOTHING)
6165     {
6166         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6167         prefs->mergeStyle("/tools/text/style", css);
6168     }
6170     sp_style_unref(query);
6172     sp_desktop_set_style (desktop, css, true, true);
6173     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6174                                    _("Text: Change alignment"));
6175     sp_repr_css_attr_unref (css);
6177     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6180 void
6181 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6182                                gpointer          data)
6184     if (g_object_get_data (G_OBJECT (button), "block")) return;
6186     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6187     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6188     int          prop       = GPOINTER_TO_INT(data);
6189     bool         active     = gtk_toggle_button_get_active (button);
6191     SPStyle *query =
6192         sp_style_new (SP_ACTIVE_DOCUMENT);
6194     int result_fontspec =
6195         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6197     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6198     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6199     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6201     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6202     Glib::ustring newFontSpec = "";
6204     if (fontSpec.empty()) {
6205         // Construct a new font specification if it does not yet exist
6206         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6207         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6208         fontFromStyle->Unref();
6209     }
6211     bool nochange = true;
6212     switch (prop)
6213     {
6214         case 0:
6215         {
6216             if (!fontSpec.empty()) {
6217                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6218                 if (!newFontSpec.empty()) {
6219                     // Don't even set the bold if the font didn't exist on the system
6220                     sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6221                     nochange = false;
6222                 }
6223             }
6224             // set or reset the button according
6225             if(nochange) {
6226                 gboolean check = gtk_toggle_button_get_active (button);
6228                 if (active != check)
6229                 {
6230                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6231                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6232                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6233                 }
6234             }
6236             break;
6237         }
6239         case 1:
6240         {
6241             if (!fontSpec.empty()) {
6242                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6243                 if (!newFontSpec.empty()) {
6244                     // Don't even set the italic if the font didn't exist on the system
6245                     sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6246                     nochange = false;
6247                 }
6248             }
6249             if(nochange) {
6250                 gboolean check = gtk_toggle_button_get_active (button);
6252                 if (active != check)
6253                 {
6254                     g_object_set_data (G_OBJECT (button), "block", gpointer(1));
6255                     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), active);
6256                     g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6257                 }
6258             }
6259             break;
6260         }
6261     }
6263     if (!newFontSpec.empty()) {
6264         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6265     }
6267     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6268     if (result_fontspec == QUERY_STYLE_NOTHING)
6269     {
6270         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6271         prefs->mergeStyle("/tools/text/style", css);
6272     }
6274     sp_style_unref(query);
6276     sp_desktop_set_style (desktop, css, true, true);
6277     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6278                                    _("Text: Change font style"));
6279     sp_repr_css_attr_unref (css);
6281     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6284 void
6285 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6286                                      gpointer         data)
6288     if (g_object_get_data (G_OBJECT (button), "block")) {
6289         return;
6290     }
6292     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6293     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6294     int          prop       = GPOINTER_TO_INT(data);
6296     switch (prop)
6297     {
6298         case 0:
6299         {
6300             sp_repr_css_set_property (css, "writing-mode", "lr");
6301             break;
6302         }
6304         case 1:
6305         {
6306             sp_repr_css_set_property (css, "writing-mode", "tb");
6307             break;
6308         }
6309     }
6311     SPStyle *query =
6312         sp_style_new (SP_ACTIVE_DOCUMENT);
6313     int result_numbers =
6314         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6316     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6317     if (result_numbers == QUERY_STYLE_NOTHING)
6318     {
6319         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6320         prefs->mergeStyle("/tools/text/style", css);
6321     }
6323     sp_desktop_set_style (desktop, css, true, true);
6324     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6325                                    _("Text: Change orientation"));
6326     sp_repr_css_attr_unref (css);
6328     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6331 gboolean
6332 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6334     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6335     if (!desktop) return FALSE;
6337     switch (get_group0_keyval (event)) {
6338         case GDK_KP_Enter: // chosen
6339         case GDK_Return:
6340             // unfreeze and update, which will defocus
6341             g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6342             sp_text_toolbox_family_changed (NULL, tbl);
6343             return TRUE; // I consumed the event
6344             break;
6345         case GDK_Escape:
6346             // defocus
6347             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6348             return TRUE; // I consumed the event
6349             break;
6350     }
6351     return FALSE;
6354 gboolean
6355 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6357     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6358     if (!desktop) return FALSE;
6360     switch (get_group0_keyval (event)) {
6361         case GDK_KP_Enter:
6362         case GDK_Return:
6363         case GDK_Escape: // defocus
6364             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6365             return TRUE; // I consumed the event
6366             break;
6367         case GDK_w:
6368         case GDK_W:
6369             if (event->state & GDK_CONTROL_MASK) {
6370                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6371                 return TRUE; // I consumed the event
6372             }
6373             break;
6374     }
6375     return FALSE;
6379 void
6380 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6381                                GObject     *tbl)
6383      // quit if run by the _changed callbacks
6384     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6385         return;
6386     }
6388     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6390    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6392     // If this is not from selecting a size in the list (in which case get_active will give the
6393     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6394     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6395     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6396    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed")) {
6397         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6398         return;
6399    }
6401     gdouble value = -1;
6402     {
6403         gchar *endptr;
6404         gchar *const text = gtk_combo_box_get_active_text(cbox);
6405         if (text) {
6406             value = g_strtod(text, &endptr);
6407             if (endptr == text) {  // Conversion failed, non-numeric input.
6408                 value = -1;
6409             }
6410             g_free(text);
6411         }
6412     }
6413     if (value <= 0) {
6414         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6415         return; // could not parse value
6416     }
6418     SPCSSAttr *css = sp_repr_css_attr_new ();
6419     Inkscape::CSSOStringStream osfs;
6420     osfs << value;
6421     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6423     SPStyle *query =
6424         sp_style_new (SP_ACTIVE_DOCUMENT);
6425     int result_numbers =
6426         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6428     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6429     if (result_numbers == QUERY_STYLE_NOTHING)
6430     {
6431         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6432         prefs->mergeStyle("/tools/text/style", css);
6433     }
6435     sp_style_unref(query);
6437     sp_desktop_set_style (desktop, css, true, true);
6438     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6439                                    _("Text: Change font size"));
6440     sp_repr_css_attr_unref (css);
6442     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6444     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6447 gboolean
6448 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6450     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6451     if (!desktop) return FALSE;
6453     if (!g_object_get_data (tbl, "esc-pressed")) {
6454         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6455         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6456         sp_text_toolbox_size_changed (cbox, tbl);
6457         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6458     }
6459     return FALSE; // I consumed the event
6463 gboolean
6464 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6466     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6467     if (!desktop) return FALSE;
6469     switch (get_group0_keyval (event)) {
6470         case GDK_Escape: // defocus
6471             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6472             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6473             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6474             return TRUE; // I consumed the event
6475             break;
6476         case GDK_Return: // defocus
6477         case GDK_KP_Enter:
6478             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6479             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6480             sp_text_toolbox_size_changed (cbox, tbl);
6481             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6482             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6483             return TRUE; // I consumed the event
6484             break;
6485     }
6486     return FALSE;
6489 // While editing font name in the entry, disable family_changed by freezing, otherwise completion
6490 // does not work!
6491 gboolean
6492 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6493                                  GdkEventFocus    */*event*/,
6494                                  GObject          *tbl)
6496     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6497     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1); // select all
6498     return FALSE;
6501 gboolean
6502 sp_text_toolbox_entry_focus_out  (GtkWidget        *entry,
6503                                  GdkEventFocus    */*event*/,
6504                                  GObject          *tbl)
6506     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6507     gtk_entry_select_region (GTK_ENTRY (entry), 0, 0); // deselect
6508     return FALSE;
6511 void
6512 cell_data_func  (GtkCellLayout */*cell_layout*/,
6513                  GtkCellRenderer   *cell,
6514                  GtkTreeModel      *tree_model,
6515                  GtkTreeIter       *iter,
6516                  gpointer           /*data*/)
6518     gchar *family;
6519     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6520     gchar *const family_escaped = g_markup_escape_text(family, -1);
6522     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6523     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6525     std::stringstream markup;
6526     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6527            << family_escaped << "'>" << sample_escaped << "</span>";
6528     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6530     g_free(family);
6531     g_free(family_escaped);
6532     g_free(sample_escaped);
6535 gboolean            text_toolbox_completion_match_selected    (GtkEntryCompletion *widget,
6536                                                         GtkTreeModel       *model,
6537                                                         GtkTreeIter        *iter,
6538                                                         GObject *tbl)
6540     // We intercept this signal so as to fire family_changed at once (without it, you'd have to
6541     // press Enter again after choosing a completion)
6542     gchar *family;
6543     gtk_tree_model_get(model, iter, 0, &family, -1);
6545     GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6546     gtk_entry_set_text (GTK_ENTRY (entry), family);
6548     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6549     sp_text_toolbox_family_changed (NULL, tbl);
6550     return TRUE;
6554 static void
6555 cbe_add_completion (GtkComboBoxEntry *cbe, GObject *tbl){
6556     GtkEntry *entry;
6557     GtkEntryCompletion *completion;
6558     GtkTreeModel *model;
6560     entry = GTK_ENTRY(gtk_bin_get_child(GTK_BIN(cbe)));
6561     completion = gtk_entry_completion_new();
6562     model = gtk_combo_box_get_model(GTK_COMBO_BOX(cbe));
6563     gtk_entry_completion_set_model(completion, model);
6564     gtk_entry_completion_set_text_column(completion, 0);
6565     gtk_entry_completion_set_inline_completion(completion, FALSE);
6566     gtk_entry_completion_set_inline_selection(completion, FALSE);
6567     gtk_entry_completion_set_popup_completion(completion, TRUE);
6568     gtk_entry_set_completion(entry, completion);
6570     g_signal_connect (G_OBJECT (completion),  "match-selected", G_CALLBACK (text_toolbox_completion_match_selected), tbl);
6572     g_object_unref(completion);
6575 void        sp_text_toolbox_family_popnotify          (GtkComboBox *widget,
6576                                                        void *property,
6577                                                         GObject *tbl)
6579   // while the drop-down is open, we disable font family changing, reenabling it only when it closes
6581   gboolean shown;
6582   g_object_get (G_OBJECT(widget), "popup-shown", &shown, NULL);
6583   if (shown) {
6584          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6585          //g_print("POP: notify: SHOWN\n");
6586   } else {
6587          g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6589          // stupid GTK doesn't let us attach to events in the drop-down window, so we peek here to
6590          // find out if the drop down was closed by Enter and if so, manually update (only
6591          // necessary on Windows, on Linux it updates itself - what a mess, but we'll manage)
6592          GdkEvent *ev = gtk_get_current_event();
6593          if (ev) {
6594              //g_print ("ev type: %d\n", ev->type);
6595              if (ev->type == GDK_KEY_PRESS) {
6596                  switch (get_group0_keyval ((GdkEventKey *) ev)) {
6597                      case GDK_KP_Enter: // chosen
6598                      case GDK_Return:
6599                      {
6600                          // make sure the chosen one is inserted into the entry
6601                          GtkComboBox  *combo = GTK_COMBO_BOX (((Gtk::ComboBox *) (g_object_get_data (tbl, "family-entry-combo")))->gobj());
6602                          GtkTreeModel *model = gtk_combo_box_get_model(combo);
6603                          GtkTreeIter iter;
6604                          gboolean has_active = gtk_combo_box_get_active_iter (combo, &iter);
6605                          if (has_active) {
6606                              gchar *family;
6607                              gtk_tree_model_get(model, &iter, 0, &family, -1);
6608                              GtkEntry *entry = GTK_ENTRY (g_object_get_data (G_OBJECT (tbl), "family-entry"));
6609                              gtk_entry_set_text (GTK_ENTRY (entry), family);
6610                          }
6612                          // update
6613                          sp_text_toolbox_family_changed (NULL, tbl);
6614                          break;
6615                      }
6616                  }
6617              }
6618          }
6620          // regardless of whether we updated, defocus the widget
6621          SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6622          if (desktop)
6623              gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6624          //g_print("POP: notify: HIDDEN\n");
6625   }
6628 GtkWidget*
6629 sp_text_toolbox_new (SPDesktop *desktop)
6631     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6632     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6634     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6635     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6637     GtkTooltips *tt = gtk_tooltips_new();
6639     ////////////Family
6640     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6641     Gtk::ComboBoxEntry *font_sel = Gtk::manage(new Gtk::ComboBoxEntry(store));
6643     gtk_rc_parse_string (
6644        "style \"dropdown-as-list-style\"\n"
6645        "{\n"
6646        "    GtkComboBox::appears-as-list = 1\n"
6647        "}\n"
6648        "widget \"*.toolbox-fontfamily-list\" style \"dropdown-as-list-style\"");
6649     gtk_widget_set_name(GTK_WIDGET (font_sel->gobj()), "toolbox-fontfamily-list");
6650     gtk_tooltips_set_tip (tt, GTK_WIDGET (font_sel->gobj()), _("Select font family (Alt+X to access)"), "");
6652     g_signal_connect (G_OBJECT (font_sel->gobj()), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6654     cbe_add_completion(font_sel->gobj(), G_OBJECT(tbl));
6656     gtk_toolbar_append_widget( tbl, (GtkWidget*) font_sel->gobj(), "", "");
6657     g_object_set_data (G_OBJECT (tbl), "family-entry-combo", font_sel);
6659     // expand the field a bit so as to view more of the previews in the drop-down
6660     GtkRequisition req;
6661     gtk_widget_size_request (GTK_WIDGET (font_sel->gobj()), &req);
6662     gtk_widget_set_size_request  (GTK_WIDGET (font_sel->gobj()), req.width + 40, -1);
6664     GtkWidget* entry = (GtkWidget*) font_sel->get_entry()->gobj();
6665     g_signal_connect (G_OBJECT (entry), "activate", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6667     g_signal_connect (G_OBJECT (font_sel->gobj()), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6668     g_signal_connect (G_OBJECT (font_sel->gobj()), "notify::popup-shown",
6669              G_CALLBACK (sp_text_toolbox_family_popnotify), tbl);
6670     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6671     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6672     g_signal_connect (G_OBJECT (entry),  "focus-out-event", G_CALLBACK (sp_text_toolbox_entry_focus_out), tbl);
6674     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6675     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6677     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6678     gtk_cell_layout_clear( GTK_CELL_LAYOUT(font_sel->gobj()) );
6679     gtk_cell_layout_pack_start( GTK_CELL_LAYOUT(font_sel->gobj()) , cell , TRUE );
6680     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT(font_sel->gobj()), cell, GtkCellLayoutDataFunc (cell_data_func), NULL, NULL);
6682     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6683     GtkWidget *box = gtk_event_box_new ();
6684     gtk_container_add (GTK_CONTAINER (box), image);
6685     gtk_toolbar_append_widget( tbl, box, "", "");
6686     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6687     gtk_tooltips_set_tip (tt, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6688     gtk_widget_hide (GTK_WIDGET (box));
6689     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6691     ////////////Size
6692     gchar const *const sizes[] = {
6693         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6694         "16", "18", "20", "22", "24", "28",
6695         "32", "36", "40", "48", "56", "64", "72", "144"
6696     };
6698     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6699     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6700         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6701     }
6702     gtk_widget_set_size_request (cbox, 80, -1);
6703     gtk_toolbar_append_widget( tbl, cbox, "", "");
6704     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6705     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6706     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6707     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6709     ////////////Text anchor
6710     GtkWidget *group   = gtk_radio_button_new (NULL);
6711     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6712     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6714     // left
6715     GtkWidget *rbutton = group;
6716     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6717     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6718     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6720     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6721     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6722     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6723     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6725     // center
6726     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6727     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6728     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6729     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6731     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6732     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6733     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6734     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6736     // right
6737     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6738     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6739     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6740     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6742     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6743     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6744     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6745     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6747     // fill
6748     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6749     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6750     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6751     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6753     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6754     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6755     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6756     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6758     gtk_toolbar_append_widget( tbl, row, "", "");
6760     //spacer
6761     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6763     ////////////Text style
6764     row = gtk_hbox_new (FALSE, 4);
6766     // bold
6767     rbutton = gtk_toggle_button_new ();
6768     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6769     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_BOLD, secondarySize));
6770     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6771     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6773     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6774     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6775     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6777     // italic
6778     rbutton = gtk_toggle_button_new ();
6779     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6780     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_ITALIC, secondarySize));
6781     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6782     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6784     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6785     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6786     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6788     gtk_toolbar_append_widget( tbl, row, "", "");
6790     //spacer
6791     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6793     // Text orientation
6794     group   = gtk_radio_button_new (NULL);
6795     row     = gtk_hbox_new (FALSE, 4);
6796     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6798     // horizontal
6799     rbutton = group;
6800     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6801     gtk_container_add           (GTK_CONTAINER (rbutton),
6802                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6803     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6804     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6806     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6807     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6808     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6810     // vertical
6811     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6812     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6813     gtk_container_add           (GTK_CONTAINER (rbutton),
6814                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
6815     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6816     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6818     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6819     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6820     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6821     gtk_toolbar_append_widget( tbl, row, "", "" );
6824     //watch selection
6825     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6827     sigc::connection *c_selection_changed =
6828         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6829                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6830     pool->add_connection ("selection-changed", c_selection_changed);
6832     sigc::connection *c_selection_modified =
6833         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6834                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6835     pool->add_connection ("selection-modified", c_selection_modified);
6837     sigc::connection *c_subselection_changed =
6838         new sigc::connection (desktop->connectToolSubselectionChanged
6839                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6840     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6842     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6845     gtk_widget_show_all( GTK_WIDGET(tbl) );
6847     return GTK_WIDGET(tbl);
6848 } // end of sp_text_toolbox_new()
6850 }//<unnamed> namespace
6853 //#########################
6854 //##      Connector      ##
6855 //#########################
6857 static void sp_connector_path_set_avoid(void)
6859     cc_selection_set_avoid(true);
6863 static void sp_connector_path_set_ignore(void)
6865     cc_selection_set_avoid(false);
6870 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6872     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6873     SPDocument *doc = sp_desktop_document(desktop);
6875     if (!sp_document_get_undo_sensitive(doc))
6876     {
6877         return;
6878     }
6880     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6882     if ( !repr->attribute("inkscape:connector-spacing") &&
6883             ( adj->value == defaultConnSpacing )) {
6884         // Don't need to update the repr if the attribute doesn't
6885         // exist and it is being set to the default value -- as will
6886         // happen at startup.
6887         return;
6888     }
6890     // quit if run by the attr_changed listener
6891     if (g_object_get_data( tbl, "freeze" )) {
6892         return;
6893     }
6895     // in turn, prevent listener from responding
6896     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
6898     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6899     SP_OBJECT(desktop->namedview)->updateRepr();
6901     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6902     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6903         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6904         Geom::Matrix m = Geom::identity();
6905         avoid_item_move(&m, item);
6906     }
6908     if (items) {
6909         g_slist_free(items);
6910     }
6912     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6913             _("Change connector spacing"));
6915     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6918 static void sp_connector_graph_layout(void)
6920     if (!SP_ACTIVE_DESKTOP) return;
6921     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6923     // hack for clones, see comment in align-and-distribute.cpp
6924     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6925     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6927     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6929     prefs->setInt("/options/clonecompensation/value", saved_compensation);
6931     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6934 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6936     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6937     prefs->setBool("/tools/connector/directedlayout",
6938                 gtk_toggle_action_get_active( act ));
6941 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6943     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6944     prefs->setBool("/tools/connector/avoidoverlaplayout",
6945                 gtk_toggle_action_get_active( act ));
6949 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6951     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6952     prefs->setDouble("/tools/connector/length", adj->value);
6955 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6956                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6957                                             bool /*is_interactive*/, gpointer data)
6959     GtkWidget *tbl = GTK_WIDGET(data);
6961     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6962         return;
6963     }
6964     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6965         return;
6966     }
6968     GtkAdjustment *adj = (GtkAdjustment*)
6969             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6970     gdouble spacing = defaultConnSpacing;
6971     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6973     gtk_adjustment_set_value(adj, spacing);
6974     gtk_adjustment_value_changed(adj);
6976     spinbutton_defocus(GTK_OBJECT(tbl));
6980 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6981     NULL, /* child_added */
6982     NULL, /* child_removed */
6983     connector_tb_event_attr_changed,
6984     NULL, /* content_changed */
6985     NULL  /* order_changed */
6986 };
6989 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6991     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6992     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6994     {
6995         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6996                                           _("Avoid"),
6997                                           _("Make connectors avoid selected objects"),
6998                                           INKSCAPE_ICON_CONNECTOR_AVOID,
6999                                           secondarySize );
7000         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
7001         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7002     }
7004     {
7005         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
7006                                           _("Ignore"),
7007                                           _("Make connectors ignore selected objects"),
7008                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
7009                                           secondarySize );
7010         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
7011         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7012     }
7014     EgeAdjustmentAction* eact = 0;
7016     // Spacing spinbox
7017     eact = create_adjustment_action( "ConnectorSpacingAction",
7018                                      _("Connector Spacing"), _("Spacing:"),
7019                                      _("The amount of space left around objects by auto-routing connectors"),
7020                                      "/tools/connector/spacing", defaultConnSpacing,
7021                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
7022                                      0, 100, 1.0, 10.0,
7023                                      0, 0, 0,
7024                                      connector_spacing_changed, 1, 0 );
7025     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7027     // Graph (connector network) layout
7028     {
7029         InkAction* inky = ink_action_new( "ConnectorGraphAction",
7030                                           _("Graph"),
7031                                           _("Nicely arrange selected connector network"),
7032                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
7033                                           secondarySize );
7034         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
7035         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
7036     }
7038     // Default connector length spinbox
7039     eact = create_adjustment_action( "ConnectorLengthAction",
7040                                      _("Connector Length"), _("Length:"),
7041                                      _("Ideal length for connectors when layout is applied"),
7042                                      "/tools/connector/length", 100,
7043                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7044                                      10, 1000, 10.0, 100.0,
7045                                      0, 0, 0,
7046                                      connector_length_changed, 1, 0 );
7047     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7050     // Directed edges toggle button
7051     {
7052         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7053                                                       _("Downwards"),
7054                                                       _("Make connectors with end-markers (arrows) point downwards"),
7055                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7056                                                       Inkscape::ICON_SIZE_DECORATION );
7057         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7059         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7060         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7062         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7063     }
7065     // Avoid overlaps toggle button
7066     {
7067         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7068                                                       _("Remove overlaps"),
7069                                                       _("Do not allow overlapping shapes"),
7070                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7071                                                       Inkscape::ICON_SIZE_DECORATION );
7072         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7074         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7075         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7077         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7078     }
7080     // Code to watch for changes to the connector-spacing attribute in
7081     // the XML.
7082     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7083     g_assert(repr != NULL);
7085     purge_repr_listener( holder, holder );
7087     if (repr) {
7088         g_object_set_data( holder, "repr", repr );
7089         Inkscape::GC::anchor(repr);
7090         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7091         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7092     }
7093 } // end of sp_connector_toolbox_prep()
7096 //#########################
7097 //##     Paintbucket     ##
7098 //#########################
7100 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7102     gint channels = ege_select_one_action_get_active( act );
7103     flood_channels_set_channels( channels );
7106 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7108     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7109     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7112 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7114     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7115     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7118 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7120     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7121     SPUnit const *unit = tracker->getActiveUnit();
7122     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7124     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7125     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7128 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7130     // FIXME: make defaults settable via Inkscape Options
7131     struct KeyValue {
7132         char const *key;
7133         double value;
7134     } const key_values[] = {
7135         {"threshold", 15},
7136         {"offset", 0.0}
7137     };
7139     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7140         KeyValue const &kv = key_values[i];
7141         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7142         if ( adj ) {
7143             gtk_adjustment_set_value(adj, kv.value);
7144         }
7145     }
7147     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7148     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7149     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7150     ege_select_one_action_set_active( autogap_action, 0 );
7153 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7155     EgeAdjustmentAction* eact = 0;
7156     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7158     {
7159         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7161         GList* items = 0;
7162         gint count = 0;
7163         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7164         {
7165             GtkTreeIter iter;
7166             gtk_list_store_append( model, &iter );
7167             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7168             count++;
7169         }
7170         g_list_free( items );
7171         items = 0;
7172         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7173         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7174         ege_select_one_action_set_appearance( act1, "compact" );
7175         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7176         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7177         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7178         g_object_set_data( holder, "channels_action", act1 );
7179     }
7181     // Spacing spinbox
7182     {
7183         eact = create_adjustment_action(
7184             "ThresholdAction",
7185             _("Fill Threshold"), _("Threshold:"),
7186             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7187             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7188             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 10.0,
7189             0, 0, 0,
7190             paintbucket_threshold_changed, 1, 0 );
7192         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7193         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7194     }
7196     // Create the units menu.
7197     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7198     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7199     if (!stored_unit.empty())
7200         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7201     g_object_set_data( holder, "tracker", tracker );
7202     {
7203         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7204         gtk_action_group_add_action( mainActions, act );
7205     }
7207     // Offset spinbox
7208     {
7209         eact = create_adjustment_action(
7210             "OffsetAction",
7211             _("Grow/shrink by"), _("Grow/shrink by:"),
7212             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7213             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7214             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7215             0, 0, 0,
7216             paintbucket_offset_changed, 1, 2);
7217         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7219         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7220     }
7222     /* Auto Gap */
7223     {
7224         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7226         GList* items = 0;
7227         gint count = 0;
7228         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7229         {
7230             GtkTreeIter iter;
7231             gtk_list_store_append( model, &iter );
7232             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7233             count++;
7234         }
7235         g_list_free( items );
7236         items = 0;
7237         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7238         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7239         ege_select_one_action_set_appearance( act2, "compact" );
7240         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7241         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7242         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7243         g_object_set_data( holder, "autogap_action", act2 );
7244     }
7246     /* Reset */
7247     {
7248         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7249                                           _("Defaults"),
7250                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7251                                           GTK_STOCK_CLEAR );
7252         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7253         gtk_action_group_add_action( mainActions, act );
7254         gtk_action_set_sensitive( act, TRUE );
7255     }
7259 /*
7260   Local Variables:
7261   mode:c++
7262   c-file-style:"stroustrup"
7263   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7264   indent-tabs-mode:nil
7265   fill-column:99
7266   End:
7267 */
7268 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :