Code

Move files from the src/dialogs/ directory to the places where they
[inkscape.git] / src / widgets / toolbox.cpp
1 /** @file
2  * @brief Controls bars for some of Inkscape's tools (for some tools,
3  * they are in their own files)
4  */
5 /* Authors:
6  *   MenTaLguY <mental@rydia.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *   Frank Felfe <innerspace@iname.com>
10  *   John Cliff <simarilius@yahoo.com>
11  *   David Turner <novalis@gnu.org>
12  *   Josh Andler <scislac@scislac.com>
13  *   Jon A. Cruz <jon@joncruz.org>
14  *   Maximilian Albert <maximilian.albert@gmail.com>
15  *
16  * Copyright (C) 2004 David Turner
17  * Copyright (C) 2003 MenTaLguY
18  * Copyright (C) 1999-2008 authors
19  * Copyright (C) 2001-2002 Ximian, Inc.
20  *
21  * Released under GNU GPL, read the file 'COPYING' for more information
22  */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
28 #include <cstring>
29 #include <string>
31 #include <gtkmm.h>
32 #include <gtk/gtk.h>
33 #include <iostream>
34 #include <sstream>
35 #include <glibmm/i18n.h>
37 #include "../box3d-context.h"
38 #include "../box3d.h"
39 #include "../conn-avoid-ref.h"
40 #include "../connection-pool.h"
41 #include "../connector-context.h"
42 #include "../desktop.h"
43 #include "../desktop-handles.h"
44 #include "../desktop-style.h"
45 #include "../dialogs/dialog-events.h"
46 #include "../dialogs/text-edit.h"
47 #include "../document-private.h"
48 #include "../ege-adjustment-action.h"
49 #include "../ege-output-action.h"
50 #include "../ege-select-one-action.h"
51 #include "../flood-context.h"
52 #include "gradient-toolbar.h"
53 #include "../graphlayout/graphlayout.h"
54 #include "../helper/unit-menu.h"
55 #include "../helper/units.h"
56 #include "../helper/unit-tracker.h"
57 #include "icon.h"
58 #include "../ink-action.h"
59 #include "../inkscape.h"
60 #include "../interface.h"
61 #include "../libnrtype/font-instance.h"
62 #include "../libnrtype/font-lister.h"
63 #include "../live_effects/effect.h"
64 #include "../live_effects/lpe-angle_bisector.h"
65 #include "../live_effects/lpe-line_segment.h"
66 #include "../lpe-tool-context.h"
67 #include "../mod360.h"
68 #include "../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_EDIT_CLONE,
727         SP_VERB_EDIT_COPY,
728         SP_VERB_EDIT_CUT,
729         SP_VERB_EDIT_DUPLICATE,
730         SP_VERB_EDIT_PASTE,
731         SP_VERB_EDIT_REDO,
732         SP_VERB_EDIT_UNDO,
733         SP_VERB_EDIT_UNLINK_CLONE,
734         SP_VERB_FILE_EXPORT,
735         SP_VERB_FILE_IMPORT,
736         SP_VERB_FILE_NEW,
737         SP_VERB_FILE_OPEN,
738         SP_VERB_FILE_PRINT,
739         SP_VERB_FILE_SAVE,
740         SP_VERB_OBJECT_TO_CURVE,
741         SP_VERB_SELECTION_GROUP,
742         SP_VERB_SELECTION_OUTLINE,
743         SP_VERB_SELECTION_UNGROUP,
744         SP_VERB_ZOOM_1_1,
745         SP_VERB_ZOOM_1_2,
746         SP_VERB_ZOOM_2_1,
747         SP_VERB_ZOOM_DRAWING,
748         SP_VERB_ZOOM_IN,
749         SP_VERB_ZOOM_NEXT,
750         SP_VERB_ZOOM_OUT,
751         SP_VERB_ZOOM_PAGE,
752         SP_VERB_ZOOM_PAGE_WIDTH,
753         SP_VERB_ZOOM_PREV,
754         SP_VERB_ZOOM_SELECTION,
755     };
757     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
759     static std::map<SPDesktop*, Glib::RefPtr<Gtk::ActionGroup> > groups;
760     Glib::RefPtr<Gtk::ActionGroup> mainActions;
761     if ( groups.find(desktop) != groups.end() ) {
762         mainActions = groups[desktop];
763     }
765     if ( !mainActions ) {
766         mainActions = Gtk::ActionGroup::create("main");
767         groups[desktop] = mainActions;
768     }
770     for ( guint i = 0; i < G_N_ELEMENTS(verbsToUse); i++ ) {
771         Inkscape::Verb* verb = Inkscape::Verb::get(verbsToUse[i]);
772         if ( verb ) {
773             if (!mainActions->get_action(verb->get_id())) {
774                 GtkAction* act = create_action_for_verb( verb, view, toolboxSize );
775                 mainActions->add(Glib::wrap(act));
776             }
777         }
778     }
780     if ( !mainActions->get_action("ToolZoom") ) {
781         GtkTooltips *tt = gtk_tooltips_new();
782         for ( guint i = 0; i < G_N_ELEMENTS(tools) && tools[i].type_name; i++ ) {
783             Glib::RefPtr<VerbAction> va = VerbAction::create(Inkscape::Verb::get(tools[i].verb), Inkscape::Verb::get(tools[i].doubleclick_verb), view, tt);
784             if ( va ) {
785                 mainActions->add(va);
786                 if ( i == 0 ) {
787                     va->set_active(true);
788                 }
789             }
790         }
791     }
794     return mainActions;
798 void handlebox_detached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
800     gtk_widget_set_size_request( widget,
801                                  widget->allocation.width,
802                                  widget->allocation.height );
805 void handlebox_attached(GtkHandleBox* /*handlebox*/, GtkWidget* widget, gpointer /*userData*/)
807     gtk_widget_set_size_request( widget, -1, -1 );
812 GtkWidget *
813 sp_tool_toolbox_new()
815     GtkTooltips *tt = gtk_tooltips_new();
816     GtkWidget* tb = gtk_toolbar_new();
817     gtk_toolbar_set_orientation(GTK_TOOLBAR(tb), GTK_ORIENTATION_VERTICAL);
818     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(tb), TRUE);
820     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
821     g_object_set_data(G_OBJECT(tb), "tooltips", tt);
823     gtk_widget_set_sensitive(tb, FALSE);
825     GtkWidget *hb = gtk_handle_box_new();
826     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_TOP);
827     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
828     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
830     gtk_container_add(GTK_CONTAINER(hb), tb);
831     gtk_widget_show(GTK_WIDGET(tb));
833     sigc::connection* conn = new sigc::connection;
834     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
836     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
837     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
839     return hb;
842 GtkWidget *
843 sp_aux_toolbox_new()
845     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
847     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
849     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
851     gtk_widget_set_sensitive(tb, FALSE);
853     GtkWidget *hb = gtk_handle_box_new();
854     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
855     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
856     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
858     gtk_container_add(GTK_CONTAINER(hb), tb);
859     gtk_widget_show(GTK_WIDGET(tb));
861     sigc::connection* conn = new sigc::connection;
862     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
864     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
865     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
867     return hb;
870 //####################################
871 //# Commands Bar
872 //####################################
874 GtkWidget *
875 sp_commands_toolbox_new()
877     GtkWidget *tb = gtk_toolbar_new();
879     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
880     gtk_widget_set_sensitive(tb, FALSE);
882     GtkWidget *hb = gtk_handle_box_new();
883     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
884     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
885     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
887     gtk_container_add(GTK_CONTAINER(hb), tb);
888     gtk_widget_show(GTK_WIDGET(tb));
890     sigc::connection* conn = new sigc::connection;
891     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
893     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
894     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
896     return hb;
899 GtkWidget *
900 sp_snap_toolbox_new()
902         GtkWidget *tb = gtk_vbox_new(FALSE, 0);
903         gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
904         g_object_set_data(G_OBJECT(tb), "desktop", NULL);
906         //GtkWidget *tb = gtk_toolbar_new();
907     //g_object_set_data(G_OBJECT(tb), "desktop", NULL);
909     gtk_widget_set_sensitive(tb, FALSE);
911     GtkWidget *hb = gtk_handle_box_new();
912     gtk_handle_box_set_handle_position(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
913     gtk_handle_box_set_shadow_type(GTK_HANDLE_BOX(hb), GTK_SHADOW_OUT);
914     gtk_handle_box_set_snap_edge(GTK_HANDLE_BOX(hb), GTK_POS_LEFT);
916     gtk_container_add(GTK_CONTAINER(hb), tb);
917     gtk_widget_show(GTK_WIDGET(tb));
919     sigc::connection* conn = new sigc::connection;
920     g_object_set_data(G_OBJECT(hb), "event_context_connection", conn);
922     g_signal_connect(G_OBJECT(hb), "child_detached", G_CALLBACK(handlebox_detached), static_cast<gpointer>(0));
923     g_signal_connect(G_OBJECT(hb), "child_attached", G_CALLBACK(handlebox_attached), static_cast<gpointer>(0));
925     return hb;
928 static EgeAdjustmentAction * create_adjustment_action( gchar const *name,
929                                                        gchar const *label, gchar const *shortLabel, gchar const *tooltip,
930                                                        Glib::ustring const &path, gdouble def,
931                                                        GtkWidget *focusTarget,
932                                                        GtkWidget *us,
933                                                        GObject *dataKludge,
934                                                        gboolean altx, gchar const *altx_mark,
935                                                        gdouble lower, gdouble upper, gdouble step, gdouble page,
936                                                        gchar const** descrLabels, gdouble const* descrValues, guint descrCount,
937                                                        void (*callback)(GtkAdjustment *, GObject *),
938                                                        gdouble climb = 0.1, guint digits = 3, double factor = 1.0 )
940     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
941     GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( prefs->getDouble(path, def) * factor,
942                                                              lower, upper, step, page, 0 ) );
943     if (us) {
944         sp_unit_selector_add_adjustment( SP_UNIT_SELECTOR(us), adj );
945     }
947     gtk_signal_connect( GTK_OBJECT(adj), "value-changed", GTK_SIGNAL_FUNC(callback), dataKludge );
949     EgeAdjustmentAction* act = ege_adjustment_action_new( adj, name, label, tooltip, 0, climb, digits );
950     if ( shortLabel ) {
951         g_object_set( act, "short_label", shortLabel, NULL );
952     }
954     if ( (descrCount > 0) && descrLabels && descrValues ) {
955         ege_adjustment_action_set_descriptions( act, descrLabels, descrValues, descrCount );
956     }
958     if ( focusTarget ) {
959         ege_adjustment_action_set_focuswidget( act, focusTarget );
960     }
962     if ( altx && altx_mark ) {
963         g_object_set( G_OBJECT(act), "self-id", altx_mark, NULL );
964     }
966     if ( dataKludge ) {
967         // Rather lame, but it's the only place where we need to get the entry name
968         // but we don't have an Entry
969         g_object_set_data( dataKludge, prefs->getEntry(path).getEntryName().data(), adj );
970     }
972     // Using a cast just to make sure we pass in the right kind of function pointer
973     g_object_set( G_OBJECT(act), "tool-post", static_cast<EgeWidgetFixup>(sp_set_font_size_smaller), NULL );
975     return act;
979 //####################################
980 //# node editing callbacks
981 //####################################
983 /**
984  * FIXME: Returns current shape_editor in context. // later eliminate this function at all!
985  */
986 static ShapeEditor *get_current_shape_editor()
988     if (!SP_ACTIVE_DESKTOP) {
989         return NULL;
990     }
992     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
994     if (!SP_IS_NODE_CONTEXT(event_context)) {
995         return NULL;
996     }
998     return event_context->shape_editor;
1002 void
1003 sp_node_path_edit_add(void)
1005     ShapeEditor *shape_editor = get_current_shape_editor();
1006     if (shape_editor) shape_editor->add_node();
1009 void
1010 sp_node_path_edit_delete(void)
1012     ShapeEditor *shape_editor = get_current_shape_editor();
1013     if (shape_editor) shape_editor->delete_nodes_preserving_shape();
1016 void
1017 sp_node_path_edit_delete_segment(void)
1019     ShapeEditor *shape_editor = get_current_shape_editor();
1020     if (shape_editor) shape_editor->delete_segment();
1023 void
1024 sp_node_path_edit_break(void)
1026     ShapeEditor *shape_editor = get_current_shape_editor();
1027     if (shape_editor) shape_editor->break_at_nodes();
1030 void
1031 sp_node_path_edit_join(void)
1033     ShapeEditor *shape_editor = get_current_shape_editor();
1034     if (shape_editor) shape_editor->join_nodes();
1037 void
1038 sp_node_path_edit_join_segment(void)
1040     ShapeEditor *shape_editor = get_current_shape_editor();
1041     if (shape_editor) shape_editor->join_segments();
1044 void
1045 sp_node_path_edit_toline(void)
1047     ShapeEditor *shape_editor = get_current_shape_editor();
1048     if (shape_editor) shape_editor->set_type_of_segments(NR_LINETO);
1051 void
1052 sp_node_path_edit_tocurve(void)
1054     ShapeEditor *shape_editor = get_current_shape_editor();
1055     if (shape_editor) shape_editor->set_type_of_segments(NR_CURVETO);
1058 void
1059 sp_node_path_edit_cusp(void)
1061     ShapeEditor *shape_editor = get_current_shape_editor();
1062     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_CUSP);
1065 void
1066 sp_node_path_edit_smooth(void)
1068     ShapeEditor *shape_editor = get_current_shape_editor();
1069     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SMOOTH);
1072 void
1073 sp_node_path_edit_symmetrical(void)
1075     ShapeEditor *shape_editor = get_current_shape_editor();
1076     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_SYMM);
1079 void
1080 sp_node_path_edit_auto(void)
1082     ShapeEditor *shape_editor = get_current_shape_editor();
1083     if (shape_editor) shape_editor->set_node_type(Inkscape::NodePath::NODE_AUTO);
1086 static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
1087     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1088     bool show = gtk_toggle_action_get_active( act );
1089     prefs->setBool("/tools/nodes/show_handles",  show);
1090     ShapeEditor *shape_editor = get_current_shape_editor();
1091     if (shape_editor) shape_editor->show_handles(show);
1094 static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
1095     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1096     bool show = gtk_toggle_action_get_active( act );
1097     prefs->setBool("/tools/nodes/show_helperpath",  show);
1098     ShapeEditor *shape_editor = get_current_shape_editor();
1099     if (shape_editor) shape_editor->show_helperpath(show);
1102 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
1103     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
1106 void sp_node_path_edit_clippath (GtkAction */*act*/, gpointer data) {
1107     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), true);
1110 void sp_node_path_edit_maskpath (GtkAction */*act*/, gpointer data) {
1111     sp_selection_edit_clip_or_mask( reinterpret_cast<SPDesktop*>(data), false);
1114 /* is called when the node selection is modified */
1115 static void
1116 sp_node_toolbox_coord_changed(gpointer /*shape_editor*/, GObject *tbl)
1118     GtkAction* xact = GTK_ACTION( g_object_get_data( tbl, "nodes_x_action" ) );
1119     GtkAction* yact = GTK_ACTION( g_object_get_data( tbl, "nodes_y_action" ) );
1120     GtkAdjustment *xadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(xact));
1121     GtkAdjustment *yadj = ege_adjustment_action_get_adjustment(EGE_ADJUSTMENT_ACTION(yact));
1123     // quit if run by the attr_changed listener
1124     if (g_object_get_data( tbl, "freeze" )) {
1125         return;
1126     }
1128     // in turn, prevent listener from responding
1129     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1131     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
1132     SPUnit const *unit = tracker->getActiveUnit();
1134     ShapeEditor *shape_editor = get_current_shape_editor();
1135     if (shape_editor && shape_editor->has_nodepath()) {
1136         Inkscape::NodePath::Path *nodepath = shape_editor->get_nodepath();
1137         int n_selected = 0;
1138         if (nodepath) {
1139             n_selected = nodepath->numSelected();
1140         }
1142         if (n_selected == 0) {
1143             gtk_action_set_sensitive(xact, FALSE);
1144             gtk_action_set_sensitive(yact, FALSE);
1145         } else {
1146             gtk_action_set_sensitive(xact, TRUE);
1147             gtk_action_set_sensitive(yact, TRUE);
1148             Geom::Coord oldx = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1149             Geom::Coord oldy = sp_units_get_pixels(gtk_adjustment_get_value(xadj), *unit);
1151             if (n_selected == 1) {
1152                 Geom::Point sel_node = nodepath->singleSelectedCoords();
1153                 if (oldx != sel_node[Geom::X] || oldy != sel_node[Geom::Y]) {
1154                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(sel_node[Geom::X], *unit));
1155                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(sel_node[Geom::Y], *unit));
1156                 }
1157             } else {
1158                 boost::optional<Geom::Coord> x = sp_node_selected_common_coord(nodepath, Geom::X);
1159                 boost::optional<Geom::Coord> y = sp_node_selected_common_coord(nodepath, Geom::Y);
1160                 if ((x && ((*x) != oldx)) || (y && ((*y) != oldy))) {
1161                     /* Note: Currently x and y will always have a value, even if the coordinates of the
1162                        selected nodes don't coincide (in this case we use the coordinates of the center
1163                        of the bounding box). So the entries are never set to zero. */
1164                     // FIXME: Maybe we should clear the entry if several nodes are selected
1165                     //        instead of providing a kind of average value
1166                     gtk_adjustment_set_value(xadj, sp_pixels_get_units(x ? (*x) : 0.0, *unit));
1167                     gtk_adjustment_set_value(yadj, sp_pixels_get_units(y ? (*y) : 0.0, *unit));
1168                 }
1169             }
1170         }
1171     } else {
1172         // no shape-editor or nodepath yet (when we just switched to the tool); coord entries must be inactive
1173         gtk_action_set_sensitive(xact, FALSE);
1174         gtk_action_set_sensitive(yact, FALSE);
1175     }
1177     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1180 static void
1181 sp_node_path_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name)
1183     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
1184     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1186     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
1187     SPUnit const *unit = tracker->getActiveUnit();
1189     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
1190         prefs->setDouble(Glib::ustring("/tools/nodes/") + value_name, sp_units_get_pixels(adj->value, *unit));
1191     }
1193     // quit if run by the attr_changed listener
1194     if (g_object_get_data( tbl, "freeze" )) {
1195         return;
1196     }
1198     // in turn, prevent listener from responding
1199     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
1201     ShapeEditor *shape_editor = get_current_shape_editor();
1202     if (shape_editor && shape_editor->has_nodepath()) {
1203         double val = sp_units_get_pixels(gtk_adjustment_get_value(adj), *unit);
1204         if (!strcmp(value_name, "x")) {
1205             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::X);
1206         }
1207         if (!strcmp(value_name, "y")) {
1208             sp_node_selected_move_absolute(shape_editor->get_nodepath(), val, Geom::Y);
1209         }
1210     }
1212     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
1215 static void
1216 sp_node_path_x_value_changed(GtkAdjustment *adj, GObject *tbl)
1218     sp_node_path_value_changed(adj, tbl, "x");
1221 static void
1222 sp_node_path_y_value_changed(GtkAdjustment *adj, GObject *tbl)
1224     sp_node_path_value_changed(adj, tbl, "y");
1227 void
1228 sp_node_toolbox_sel_changed (Inkscape::Selection *selection, GObject *tbl)
1230     {
1231     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_lpeedit" ) );
1232     SPItem *item = selection->singleItem();
1233     if (item && SP_IS_LPE_ITEM(item)) {
1234        if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item))) {
1235            gtk_action_set_sensitive(w, TRUE);
1236        } else {
1237            gtk_action_set_sensitive(w, FALSE);
1238        }
1239     } else {
1240        gtk_action_set_sensitive(w, FALSE);
1241     }
1242     }
1244     {
1245     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_clippathedit" ) );
1246     SPItem *item = selection->singleItem();
1247     if (item && item->clip_ref && item->clip_ref->getObject()) {
1248        gtk_action_set_sensitive(w, TRUE);
1249     } else {
1250        gtk_action_set_sensitive(w, FALSE);
1251     }
1252     }
1254     {
1255     GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "nodes_maskedit" ) );
1256     SPItem *item = selection->singleItem();
1257     if (item && item->mask_ref && item->mask_ref->getObject()) {
1258        gtk_action_set_sensitive(w, TRUE);
1259     } else {
1260        gtk_action_set_sensitive(w, FALSE);
1261     }
1262     }
1265 void
1266 sp_node_toolbox_sel_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
1268     sp_node_toolbox_sel_changed (selection, tbl);
1273 //################################
1274 //##    Node Editing Toolbox    ##
1275 //################################
1277 static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
1279     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1280     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
1281     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
1282     g_object_set_data( holder, "tracker", tracker );
1284     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
1286     {
1287         InkAction* inky = ink_action_new( "NodeInsertAction",
1288                                           _("Insert node"),
1289                                           _("Insert new nodes into selected segments"),
1290                                           INKSCAPE_ICON_NODE_ADD,
1291                                           secondarySize );
1292         g_object_set( inky, "short_label", _("Insert"), NULL );
1293         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_add), 0 );
1294         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1295     }
1297     {
1298         InkAction* inky = ink_action_new( "NodeDeleteAction",
1299                                           _("Delete node"),
1300                                           _("Delete selected nodes"),
1301                                           INKSCAPE_ICON_NODE_DELETE,
1302                                           secondarySize );
1303         g_object_set( inky, "short_label", _("Delete"), NULL );
1304         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete), 0 );
1305         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1306     }
1308     {
1309         InkAction* inky = ink_action_new( "NodeJoinAction",
1310                                           _("Join endnodes"),
1311                                           _("Join selected endnodes"),
1312                                           INKSCAPE_ICON_NODE_JOIN,
1313                                           secondarySize );
1314         g_object_set( inky, "short_label", _("Join"), NULL );
1315         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join), 0 );
1316         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1317     }
1319     {
1320         InkAction* inky = ink_action_new( "NodeBreakAction",
1321                                           _("Break nodes"),
1322                                           _("Break path at selected nodes"),
1323                                           INKSCAPE_ICON_NODE_BREAK,
1324                                           secondarySize );
1325         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_break), 0 );
1326         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1327     }
1330     {
1331         InkAction* inky = ink_action_new( "NodeJoinSegmentAction",
1332                                           _("Join with segment"),
1333                                           _("Join selected endnodes with a new segment"),
1334                                           INKSCAPE_ICON_NODE_JOIN_SEGMENT,
1335                                           secondarySize );
1336         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_join_segment), 0 );
1337         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1338     }
1340     {
1341         InkAction* inky = ink_action_new( "NodeDeleteSegmentAction",
1342                                           _("Delete segment"),
1343                                           _("Delete segment between two non-endpoint nodes"),
1344                                           INKSCAPE_ICON_NODE_DELETE_SEGMENT,
1345                                           secondarySize );
1346         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_delete_segment), 0 );
1347         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1348     }
1350     {
1351         InkAction* inky = ink_action_new( "NodeCuspAction",
1352                                           _("Node Cusp"),
1353                                           _("Make selected nodes corner"),
1354                                           INKSCAPE_ICON_NODE_TYPE_CUSP,
1355                                           secondarySize );
1356         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_cusp), 0 );
1357         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1358     }
1360     {
1361         InkAction* inky = ink_action_new( "NodeSmoothAction",
1362                                           _("Node Smooth"),
1363                                           _("Make selected nodes smooth"),
1364                                           INKSCAPE_ICON_NODE_TYPE_SMOOTH,
1365                                           secondarySize );
1366         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_smooth), 0 );
1367         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1368     }
1370     {
1371         InkAction* inky = ink_action_new( "NodeSymmetricAction",
1372                                           _("Node Symmetric"),
1373                                           _("Make selected nodes symmetric"),
1374                                           INKSCAPE_ICON_NODE_TYPE_SYMMETRIC,
1375                                           secondarySize );
1376         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_symmetrical), 0 );
1377         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1378     }
1380     {
1381         InkAction* inky = ink_action_new( "NodeAutoAction",
1382                                           _("Node Auto"),
1383                                           _("Make selected nodes auto-smooth"),
1384                                           INKSCAPE_ICON_NODE_TYPE_AUTO_SMOOTH,
1385                                           secondarySize );
1386         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_auto), 0 );
1387         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1388     }
1390     {
1391         InkAction* inky = ink_action_new( "NodeLineAction",
1392                                           _("Node Line"),
1393                                           _("Make selected segments lines"),
1394                                           INKSCAPE_ICON_NODE_SEGMENT_LINE,
1395                                           secondarySize );
1396         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_toline), 0 );
1397         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1398     }
1400     {
1401         InkAction* inky = ink_action_new( "NodeCurveAction",
1402                                           _("Node Curve"),
1403                                           _("Make selected segments curves"),
1404                                           INKSCAPE_ICON_NODE_SEGMENT_CURVE,
1405                                           secondarySize );
1406         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_tocurve), 0 );
1407         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1408     }
1410     {
1411         InkToggleAction* act = ink_toggle_action_new( "NodesShowHandlesAction",
1412                                                       _("Show Handles"),
1413                                                       _("Show the Bezier handles of selected nodes"),
1414                                                       INKSCAPE_ICON_SHOW_NODE_HANDLES,
1415                                                       Inkscape::ICON_SIZE_DECORATION );
1416         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1417         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_handles), desktop );
1418         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_handles", true) );
1419     }
1421     {
1422         InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
1423                                                       _("Show Outline"),
1424                                                       _("Show the outline of the path"),
1425                                                       INKSCAPE_ICON_SHOW_PATH_OUTLINE,
1426                                                       Inkscape::ICON_SIZE_DECORATION );
1427         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
1428         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
1429         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/nodes/show_helperpath", false) );
1430     }
1432     {
1433         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
1434                                           _("Next path effect parameter"),
1435                                           _("Show next path effect parameter for editing"),
1436                                           INKSCAPE_ICON_PATH_EFFECT_PARAMETER_NEXT,
1437                                           Inkscape::ICON_SIZE_DECORATION );
1438         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_nextLPEparam), desktop );
1439         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1440         g_object_set_data( holder, "nodes_lpeedit", inky);
1441     }
1443     {
1444         InkAction* inky = ink_action_new( "ObjectEditClipPathAction",
1445                                           _("Edit clipping path"),
1446                                           _("Edit the clipping path of the object"),
1447                                           INKSCAPE_ICON_PATH_CLIP_EDIT,
1448                                           Inkscape::ICON_SIZE_DECORATION );
1449         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_clippath), desktop );
1450         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1451         g_object_set_data( holder, "nodes_clippathedit", inky);
1452     }
1454     {
1455         InkAction* inky = ink_action_new( "ObjectEditMaskPathAction",
1456                                           _("Edit mask path"),
1457                                           _("Edit the mask of the object"),
1458                                           INKSCAPE_ICON_PATH_MASK_EDIT,
1459                                           Inkscape::ICON_SIZE_DECORATION );
1460         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_node_path_edit_maskpath), desktop );
1461         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
1462         g_object_set_data( holder, "nodes_maskedit", inky);
1463     }
1465     /* X coord of selected node(s) */
1466     {
1467         EgeAdjustmentAction* eact = 0;
1468         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1469         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1470         eact = create_adjustment_action( "NodeXAction",
1471                                          _("X coordinate:"), _("X:"), _("X coordinate of selected node(s)"),
1472                                          "/tools/nodes/Xcoord", 0,
1473                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-nodes",
1474                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1475                                          labels, values, G_N_ELEMENTS(labels),
1476                                          sp_node_path_x_value_changed );
1477         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1478         g_object_set_data( holder, "nodes_x_action", eact );
1479         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1480         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1481     }
1483     /* Y coord of selected node(s) */
1484     {
1485         EgeAdjustmentAction* eact = 0;
1486         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1487         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
1488         eact = create_adjustment_action( "NodeYAction",
1489                                          _("Y coordinate:"), _("Y:"), _("Y coordinate of selected node(s)"),
1490                                          "/tools/nodes/Ycoord", 0,
1491                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
1492                                          -1e6, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
1493                                          labels, values, G_N_ELEMENTS(labels),
1494                                          sp_node_path_y_value_changed );
1495         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
1496         g_object_set_data( holder, "nodes_y_action", eact );
1497         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
1498         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
1499     }
1501     // add the units menu
1502     {
1503         GtkAction* act = tracker->createAction( "NodeUnitsAction", _("Units"), ("") );
1504         gtk_action_group_add_action( mainActions, act );
1505     }
1508     sp_node_toolbox_sel_changed(sp_desktop_selection(desktop), holder);
1510     //watch selection
1511     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
1513     sigc::connection *c_selection_changed =
1514         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
1515                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_changed), (GObject*)holder)));
1516     pool->add_connection ("selection-changed", c_selection_changed);
1518     sigc::connection *c_selection_modified =
1519         new sigc::connection (sp_desktop_selection (desktop)->connectModified
1520                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_sel_modified), (GObject*)holder)));
1521     pool->add_connection ("selection-modified", c_selection_modified);
1523     sigc::connection *c_subselection_changed =
1524         new sigc::connection (desktop->connectToolSubselectionChanged
1525                               (sigc::bind (sigc::ptr_fun (sp_node_toolbox_coord_changed), (GObject*)holder)));
1526     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
1528     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (holder), pool);
1530     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
1531 } // end of sp_node_toolbox_prep()
1534 //########################
1535 //##    Zoom Toolbox    ##
1536 //########################
1538 static void sp_zoom_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* /*mainActions*/, GObject* /*holder*/)
1540     // no custom GtkAction setup needed
1541 } // end of sp_zoom_toolbox_prep()
1543 void
1544 sp_tool_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1546     toolbox_set_desktop(toolbox,
1547                         desktop,
1548                         setup_tool_toolbox,
1549                         update_tool_toolbox,
1550                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1551                                                                          "event_context_connection")));
1555 void
1556 sp_aux_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1558     toolbox_set_desktop(gtk_bin_get_child(GTK_BIN(toolbox)),
1559                         desktop,
1560                         setup_aux_toolbox,
1561                         update_aux_toolbox,
1562                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1563                                                                          "event_context_connection")));
1566 void
1567 sp_commands_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1569     toolbox_set_desktop(toolbox,
1570                         desktop,
1571                         setup_commands_toolbox,
1572                         update_commands_toolbox,
1573                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1574                                                                          "event_context_connection")));
1577 void
1578 sp_snap_toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop)
1580     toolbox_set_desktop(toolbox,
1581                         desktop,
1582                         setup_snap_toolbox,
1583                         update_snap_toolbox,
1584                         static_cast<sigc::connection*>(g_object_get_data(G_OBJECT(toolbox),
1585                                                                          "event_context_connection")));
1589 static void
1590 toolbox_set_desktop(GtkWidget *toolbox, SPDesktop *desktop, SetupFunction setup_func, UpdateFunction update_func, sigc::connection *conn)
1592     gpointer ptr = g_object_get_data(G_OBJECT(toolbox), "desktop");
1593     SPDesktop *old_desktop = static_cast<SPDesktop*>(ptr);
1595     if (old_desktop) {
1596         GList *children, *iter;
1598         children = gtk_container_get_children(GTK_CONTAINER(toolbox));
1599         for ( iter = children ; iter ; iter = iter->next ) {
1600             gtk_container_remove( GTK_CONTAINER(toolbox), GTK_WIDGET(iter->data) );
1601         }
1602         g_list_free(children);
1603     }
1605     g_object_set_data(G_OBJECT(toolbox), "desktop", (gpointer)desktop);
1607     if (desktop) {
1608         gtk_widget_set_sensitive(toolbox, TRUE);
1609         setup_func(toolbox, desktop);
1610         update_func(desktop, desktop->event_context, toolbox);
1611         *conn = desktop->connectEventContextChanged
1612             (sigc::bind (sigc::ptr_fun(update_func), toolbox));
1613     } else {
1614         gtk_widget_set_sensitive(toolbox, FALSE);
1615     }
1617 } // end of toolbox_set_desktop()
1620 static void
1621 setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1623     gchar const * descr =
1624         "<ui>"
1625         "  <toolbar name='ToolToolbar'>"
1626         "    <toolitem action='ToolSelector' />"
1627         "    <toolitem action='ToolNode' />"
1628         "    <toolitem action='ToolTweak' />"
1629         "    <toolitem action='ToolZoom' />"
1630         "    <toolitem action='ToolRect' />"
1631         "    <toolitem action='Tool3DBox' />"
1632         "    <toolitem action='ToolArc' />"
1633         "    <toolitem action='ToolStar' />"
1634         "    <toolitem action='ToolSpiral' />"
1635         "    <toolitem action='ToolPencil' />"
1636         "    <toolitem action='ToolPen' />"
1637         "    <toolitem action='ToolCalligraphic' />"
1638         "    <toolitem action='ToolEraser' />"
1639 //        "    <toolitem action='ToolLPETool' />"
1640         "    <toolitem action='ToolPaintBucket' />"
1641         "    <toolitem action='ToolText' />"
1642         "    <toolitem action='ToolConnector' />"
1643         "    <toolitem action='ToolGradient' />"
1644         "    <toolitem action='ToolDropper' />"
1645         "  </toolbar>"
1646         "</ui>";
1647     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1648     GtkUIManager* mgr = gtk_ui_manager_new();
1649     GError* errVal = 0;
1650     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1652     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1653     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1655     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/ToolToolbar" );
1656     if ( prefs->getBool("/toolbox/icononly", true) ) {
1657         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1658     }
1659     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1660     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1662     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_VERTICAL);
1663     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1665     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1667     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1668     if ( child ) {
1669         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1670     }
1672     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1673 //     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/tools/small");
1677 static void
1678 update_tool_toolbox( SPDesktop *desktop, SPEventContext *eventcontext, GtkWidget */*toolbox*/ )
1680     gchar const *const tname = ( eventcontext
1681                                  ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1682                                  : NULL );
1683     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1685     for (int i = 0 ; tools[i].type_name ; i++ ) {
1686         Glib::RefPtr<Gtk::Action> act = mainActions->get_action( Inkscape::Verb::get(tools[i].verb)->get_id() );
1687         if ( act ) {
1688             bool setActive = tname && !strcmp(tname, tools[i].type_name);
1689             Glib::RefPtr<VerbAction> verbAct = Glib::RefPtr<VerbAction>::cast_dynamic(act);
1690             if ( verbAct ) {
1691                 verbAct->set_active(setActive);
1692             }
1693         }
1694     }
1697 static void
1698 setup_aux_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1700     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1701     GtkSizeGroup* grouper = gtk_size_group_new( GTK_SIZE_GROUP_BOTH );
1702     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1703     GtkUIManager* mgr = gtk_ui_manager_new();
1704     GError* errVal = 0;
1705     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1706     gtk_ui_manager_add_ui_from_string( mgr, ui_descr, -1, &errVal );
1708     std::map<std::string, GtkWidget*> dataHolders;
1710     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1711         if ( aux_toolboxes[i].prep_func ) {
1712             // converted to GtkActions and UIManager
1714             GtkWidget* kludge = gtk_toolbar_new();
1715             g_object_set_data( G_OBJECT(kludge), "dtw", desktop->canvas);
1716             g_object_set_data( G_OBJECT(kludge), "desktop", desktop);
1717             dataHolders[aux_toolboxes[i].type_name] = kludge;
1718             aux_toolboxes[i].prep_func( desktop, mainActions->gobj(), G_OBJECT(kludge) );
1719         } else {
1721             GtkWidget *sub_toolbox = 0;
1722             if (aux_toolboxes[i].create_func == NULL)
1723                 sub_toolbox = sp_empty_toolbox_new(desktop);
1724             else {
1725                 sub_toolbox = aux_toolboxes[i].create_func(desktop);
1726             }
1728             gtk_size_group_add_widget( grouper, sub_toolbox );
1730             gtk_container_add(GTK_CONTAINER(toolbox), sub_toolbox);
1731             g_object_set_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name, sub_toolbox);
1733         }
1734     }
1736     // Second pass to create toolbars *after* all GtkActions are created
1737     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1738         if ( aux_toolboxes[i].prep_func ) {
1739             // converted to GtkActions and UIManager
1741             GtkWidget* kludge = dataHolders[aux_toolboxes[i].type_name];
1743             GtkWidget* holder = gtk_table_new( 1, 3, FALSE );
1744             gtk_table_attach( GTK_TABLE(holder), kludge, 2, 3, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0 );
1746             gchar* tmp = g_strdup_printf( "/ui/%s", aux_toolboxes[i].ui_name );
1747             GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, tmp );
1748             g_free( tmp );
1749             tmp = 0;
1751             if ( prefs->getBool( "/toolbox/icononly", true) ) {
1752                 gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1753             }
1755             Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1756             gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
1758             gtk_table_attach( GTK_TABLE(holder), toolBar, 0, 1, 0, 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), 0, 0 );
1760             if ( aux_toolboxes[i].swatch_verb_id != SP_VERB_INVALID ) {
1761                 Inkscape::UI::Widget::StyleSwatch *swatch = new Inkscape::UI::Widget::StyleSwatch( NULL, _(aux_toolboxes[i].swatch_tip) );
1762                 swatch->setDesktop( desktop );
1763                 swatch->setClickVerb( aux_toolboxes[i].swatch_verb_id );
1764                 swatch->setWatchedTool( aux_toolboxes[i].swatch_tool, true );
1765                 GtkWidget *swatch_ = GTK_WIDGET( swatch->gobj() );
1766                 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 );
1767             }
1769             gtk_widget_show_all( holder );
1770             sp_set_font_size_smaller( holder );
1772             gtk_size_group_add_widget( grouper, holder );
1774             gtk_container_add( GTK_CONTAINER(toolbox), holder );
1775             g_object_set_data( G_OBJECT(toolbox), aux_toolboxes[i].data_name, holder );
1776         }
1777     }
1779     g_object_unref( G_OBJECT(grouper) );
1782 static void
1783 update_aux_toolbox(SPDesktop */*desktop*/, SPEventContext *eventcontext, GtkWidget *toolbox)
1785     gchar const *tname = ( eventcontext
1786                            ? gtk_type_name(GTK_OBJECT_TYPE(eventcontext))
1787                            : NULL );
1788     for (int i = 0 ; aux_toolboxes[i].type_name ; i++ ) {
1789         GtkWidget *sub_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), aux_toolboxes[i].data_name));
1790         if (tname && !strcmp(tname, aux_toolboxes[i].type_name)) {
1791             gtk_widget_show_all(sub_toolbox);
1792             g_object_set_data(G_OBJECT(toolbox), "shows", sub_toolbox);
1793         } else {
1794             gtk_widget_hide(sub_toolbox);
1795         }
1796     }
1799 static void
1800 setup_commands_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1802     gchar const * descr =
1803         "<ui>"
1804         "  <toolbar name='CommandsToolbar'>"
1805         "    <toolitem action='FileNew' />"
1806         "    <toolitem action='FileOpen' />"
1807         "    <toolitem action='FileSave' />"
1808         "    <toolitem action='FilePrint' />"
1809         "    <separator />"
1810         "    <toolitem action='FileImport' />"
1811         "    <toolitem action='FileExport' />"
1812         "    <separator />"
1813         "    <toolitem action='EditUndo' />"
1814         "    <toolitem action='EditRedo' />"
1815         "    <separator />"
1816         "    <toolitem action='EditCopy' />"
1817         "    <toolitem action='EditCut' />"
1818         "    <toolitem action='EditPaste' />"
1819         "    <separator />"
1820         "    <toolitem action='ZoomSelection' />"
1821         "    <toolitem action='ZoomDrawing' />"
1822         "    <toolitem action='ZoomPage' />"
1823         "    <separator />"
1824         "    <toolitem action='EditDuplicate' />"
1825         "    <toolitem action='EditClone' />"
1826         "    <toolitem action='EditUnlinkClone' />"
1827         "    <separator />"
1828         "    <toolitem action='SelectionGroup' />"
1829         "    <toolitem action='SelectionUnGroup' />"
1830         "    <separator />"
1831         "    <toolitem action='DialogFillStroke' />"
1832         "    <toolitem action='DialogText' />"
1833         "    <toolitem action='DialogXMLEditor' />"
1834         "    <toolitem action='DialogAlignDistribute' />"
1835         "    <separator />"
1836         "    <toolitem action='DialogPreferences' />"
1837         "    <toolitem action='DialogDocumentProperties' />"
1838         "  </toolbar>"
1839         "</ui>";
1840     Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions( desktop );
1841     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1843     GtkUIManager* mgr = gtk_ui_manager_new();
1844     GError* errVal = 0;
1846     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
1847     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
1849     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/CommandsToolbar" );
1850     if ( prefs->getBool("/toolbox/icononly", true) ) {
1851         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
1852     }
1854     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/small");
1855     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), (GtkIconSize)toolboxSize );
1857     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
1858     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
1861     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
1863     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
1864     if ( child ) {
1865         gtk_container_remove( GTK_CONTAINER(toolbox), child );
1866     }
1868     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
1871 static void
1872 update_commands_toolbox(SPDesktop */*desktop*/, SPEventContext */*eventcontext*/, GtkWidget */*toolbox*/)
1876 void toggle_snap_callback (GtkToggleAction *act, gpointer data) { //data points to the toolbox
1878         if (g_object_get_data(G_OBJECT(data), "freeze" )) {
1879                 return;
1880         }
1882         gpointer ptr = g_object_get_data(G_OBJECT(data), "desktop");
1883         g_assert(ptr != NULL);
1885         SPDesktop *dt = reinterpret_cast<SPDesktop*>(ptr);
1886         SPNamedView *nv = sp_desktop_namedview(dt);
1888         if (dt == NULL || nv == NULL) {
1889                 g_warning("No desktop or namedview specified (in toggle_snap_callback)!");
1890                 return;
1891         }
1893         Inkscape::XML::Node *repr = SP_OBJECT_REPR(nv);
1895         if (repr == NULL) {
1896                 g_warning("This namedview doesn't have a xml representation attached!");
1897                 return;
1898         }
1900         bool v = false;
1901         SPAttributeEnum attr = (SPAttributeEnum) GPOINTER_TO_INT(g_object_get_data(G_OBJECT(act), "SP_ATTR_INKSCAPE"));
1903         switch (attr) {
1904         case SP_ATTR_INKSCAPE_SNAP_GLOBAL:
1905                 dt->toggleSnapGlobal();
1906                 break;
1907         case SP_ATTR_INKSCAPE_SNAP_BBOX:
1908                 v = nv->snap_manager.snapprefs.getSnapModeBBox();
1909                 sp_repr_set_boolean(repr, "inkscape:snap-bbox", !v);
1910                 break;
1911         case SP_ATTR_INKSCAPE_BBOX_PATHS:
1912                 v = nv->snap_manager.snapprefs.getSnapToBBoxPath();
1913                 sp_repr_set_boolean(repr, "inkscape:bbox-paths", !v);
1914                 break;
1915         case SP_ATTR_INKSCAPE_BBOX_NODES:
1916                 v = nv->snap_manager.snapprefs.getSnapToBBoxNode();
1917                 sp_repr_set_boolean(repr, "inkscape:bbox-nodes", !v);
1918                 break;
1919         case SP_ATTR_INKSCAPE_SNAP_NODES:
1920                 v = nv->snap_manager.snapprefs.getSnapModeNode();
1921                 sp_repr_set_boolean(repr, "inkscape:snap-nodes", !v);
1922                 break;
1923         case SP_ATTR_INKSCAPE_OBJECT_PATHS:
1924                 v = nv->snap_manager.snapprefs.getSnapToItemPath();
1925                 sp_repr_set_boolean(repr, "inkscape:object-paths", !v);
1926                 break;
1927         case SP_ATTR_INKSCAPE_OBJECT_NODES:
1928                 v = nv->snap_manager.snapprefs.getSnapToItemNode();
1929                 sp_repr_set_boolean(repr, "inkscape:object-nodes", !v);
1930                 break;
1931         case SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES:
1932                 v = nv->snap_manager.snapprefs.getSnapSmoothNodes();
1933                 sp_repr_set_boolean(repr, "inkscape:snap-smooth-nodes", !v);
1934                 break;
1935         case SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS:
1936                 v = nv->snap_manager.snapprefs.getSnapIntersectionCS();
1937                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-paths", !v);
1938                 break;
1939         case SP_ATTR_INKSCAPE_SNAP_CENTER:
1940                 v = nv->snap_manager.snapprefs.getIncludeItemCenter();
1941                 sp_repr_set_boolean(repr, "inkscape:snap-center", !v);
1942                 break;
1943         case SP_ATTR_INKSCAPE_SNAP_GRIDS:
1944                 v = nv->snap_manager.snapprefs.getSnapToGrids();
1945                 sp_repr_set_boolean(repr, "inkscape:snap-grids", !v);
1946                 break;
1947         case SP_ATTR_INKSCAPE_SNAP_TO_GUIDES:
1948                 v = nv->snap_manager.snapprefs.getSnapToGuides();
1949                 sp_repr_set_boolean(repr, "inkscape:snap-to-guides", !v);
1950                 break;
1951         case SP_ATTR_INKSCAPE_SNAP_PAGE:
1952                 v = nv->snap_manager.snapprefs.getSnapToPageBorder();
1953                 sp_repr_set_boolean(repr, "inkscape:snap-page", !v);
1954                 break;
1955         /*case SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE:
1956                 v = nv->snap_manager.snapprefs.getSnapIntersectionGG();
1957                 sp_repr_set_boolean(repr, "inkscape:snap-intersection-grid-guide", !v);
1958                 break;*/
1959         case SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS:
1960                 v = nv->snap_manager.snapprefs.getSnapLineMidpoints();
1961                 sp_repr_set_boolean(repr, "inkscape:snap-midpoints", !v);
1962                 break;
1963         case SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS:
1964                 v = nv->snap_manager.snapprefs.getSnapObjectMidpoints();
1965                 sp_repr_set_boolean(repr, "inkscape:snap-object-midpoints", !v);
1966                 break;
1967         case SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS:
1968                 v = nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints();
1969                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-edge-midpoints", !v);
1970                 break;
1971         case SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS:
1972                 v = nv->snap_manager.snapprefs.getSnapBBoxMidpoints();
1973                 sp_repr_set_boolean(repr, "inkscape:snap-bbox-midpoints", !v);
1974                 break;
1975         default:
1976                 g_warning("toggle_snap_callback has been called with an ID for which no action has been defined");
1977                 break;
1978         }
1980         // The snapping preferences are stored in the document, and therefore toggling makes the document dirty
1981         SPDocument *doc = SP_OBJECT_DOCUMENT(nv);
1982         doc->setModifiedSinceSave();
1985 void setup_snap_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
1987         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1988         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
1990         gchar const * descr =
1991                 "<ui>"
1992                 "  <toolbar name='SnapToolbar'>"
1993                 "    <toolitem action='ToggleSnapGlobal' />"
1994                         "    <separator />"
1995                         "    <toolitem action='ToggleSnapFromBBoxCorner' />"
1996                         "    <toolitem action='ToggleSnapToBBoxPath' />"
1997                         "    <toolitem action='ToggleSnapToBBoxNode' />"
1998                         "    <toolitem action='ToggleSnapToFromBBoxEdgeMidpoints' />"
1999                         "    <toolitem action='ToggleSnapToFromBBoxCenters' />"
2000                         "    <separator />"
2001                         "    <toolitem action='ToggleSnapFromNode' />"
2002                         "    <toolitem action='ToggleSnapToItemPath' />"
2003                         "    <toolitem action='ToggleSnapToPathIntersections' />"
2004                         "    <toolitem action='ToggleSnapToItemNode' />"
2005                         "    <toolitem action='ToggleSnapToSmoothNodes' />"
2006                         "    <toolitem action='ToggleSnapToFromLineMidpoints' />"
2007                         "    <toolitem action='ToggleSnapToFromObjectCenters' />"
2008                         "    <toolitem action='ToggleSnapToFromRotationCenter' />"
2009                         "    <separator />"
2010                         "    <toolitem action='ToggleSnapToPageBorder' />"
2011                         "    <toolitem action='ToggleSnapToGrids' />"
2012                         "    <toolitem action='ToggleSnapToGuides' />"
2013                   //"    <toolitem action='ToggleSnapToGridGuideIntersections' />"
2014                         "  </toolbar>"
2015                 "</ui>";
2017         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2019         {
2020                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapGlobal", 
2021                     _("Snap"), _("Enable snapping"), INKSCAPE_ICON_SNAP, secondarySize,
2022                     SP_ATTR_INKSCAPE_SNAP_GLOBAL);
2024                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2025                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2026         }
2028         {
2029                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromBBoxCorner",
2030                     _("Bounding box"), _("Snap bounding box corners"), INKSCAPE_ICON_SNAP_BOUNDING_BOX,
2031                     secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX);
2033                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2034                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2035         }
2037         {
2038                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxPath",
2039                     _("Bounding box edges"), _("Snap to edges of a bounding box"),
2040                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_EDGES, secondarySize, SP_ATTR_INKSCAPE_BBOX_PATHS);
2042                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2043                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2044         }
2046         {
2047                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToBBoxNode",
2048                     _("Bounding box corners"), _("Snap to bounding box corners"),
2049                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CORNERS, secondarySize, SP_ATTR_INKSCAPE_BBOX_NODES);
2051                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2052                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2053         }
2055         {
2056                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxEdgeMidpoints",
2057                     _("BBox Edge Midpoints"), _("Snap from and to midpoints of bounding box edges"),
2058                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_MIDPOINTS, secondarySize,
2059                     SP_ATTR_INKSCAPE_SNAP_BBOX_EDGE_MIDPOINTS);
2061                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2062                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2063         }
2065         {
2066                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromBBoxCenters",
2067                     _("BBox Centers"), _("Snapping from and to centers of bounding boxes"),
2068                     INKSCAPE_ICON_SNAP_BOUNDING_BOX_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_BBOX_MIDPOINTS);
2070                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2071                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2072         }
2074         {
2075                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapFromNode",
2076                     _("Nodes"), _("Snap nodes"), INKSCAPE_ICON_SNAP_NODES, secondarySize, SP_ATTR_INKSCAPE_SNAP_NODES);
2078                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2079                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2080         }
2082         {
2083                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemPath",
2084                     _("Paths"), _("Snap to paths"), INKSCAPE_ICON_SNAP_NODES_PATH, secondarySize,
2085                     SP_ATTR_INKSCAPE_OBJECT_PATHS);
2087                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2088                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2089         }
2091         {
2092                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPathIntersections",
2093                     _("Path intersections"), _("Snap to path intersections"),
2094                     INKSCAPE_ICON_SNAP_NODES_INTERSECTION, secondarySize, SP_ATTR_INKSCAPE_SNAP_INTERS_PATHS);
2096                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2097                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2098         }
2100         {
2101                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToItemNode",
2102                     _("To nodes"), _("Snap to cusp nodes"), INKSCAPE_ICON_SNAP_NODES_CUSP, secondarySize,
2103                     SP_ATTR_INKSCAPE_OBJECT_NODES);
2105                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2106                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2107         }
2109         {
2110                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToSmoothNodes",
2111                     _("Smooth nodes"), _("Snap to smooth nodes"), INKSCAPE_ICON_SNAP_NODES_SMOOTH,
2112                     secondarySize, SP_ATTR_INKSCAPE_SNAP_SMOOTH_NODES);
2114                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2115                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2116         }
2118         {
2119                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromLineMidpoints",
2120                     _("Line Midpoints"), _("Snap from and to midpoints of line segments"),
2121                     INKSCAPE_ICON_SNAP_NODES_MIDPOINT, secondarySize, SP_ATTR_INKSCAPE_SNAP_LINE_MIDPOINTS);
2123                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2124                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2125         }
2127         {
2128                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromObjectCenters",
2129                     _("Object Centers"), _("Snap from and to centers of objects"),
2130                     INKSCAPE_ICON_SNAP_NODES_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_OBJECT_MIDPOINTS);
2132                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2133                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2134         }
2136         {
2137                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToFromRotationCenter",
2138                     _("Rotation Centers"), _("Snap from and to an item's rotation center"),
2139                     INKSCAPE_ICON_SNAP_NODES_ROTATION_CENTER, secondarySize, SP_ATTR_INKSCAPE_SNAP_CENTER);
2141                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2142                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2143         }
2145         {
2146                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToPageBorder",
2147                     _("Page border"), _("Snap to the page border"), INKSCAPE_ICON_SNAP_PAGE,
2148                     secondarySize, SP_ATTR_INKSCAPE_SNAP_PAGE);
2150                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2151                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2152         }
2154         {
2155                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGrids",
2156                     _("Grids"), _("Snap to grids"), INKSCAPE_ICON_GRID_RECTANGULAR, secondarySize,
2157                     SP_ATTR_INKSCAPE_SNAP_GRIDS);
2159                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2160                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2161         }
2163         {
2164                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGuides",
2165                     _("Guides"), _("Snap to guides"), INKSCAPE_ICON_GUIDES, secondarySize,
2166                     SP_ATTR_INKSCAPE_SNAP_TO_GUIDES);
2168                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2169                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2170         }
2172         /*{
2173                 InkToggleAction* act = ink_toggle_action_new("ToggleSnapToGridGuideIntersections",
2174                     _("Grid/guide intersections"), _("Snap to intersections of a grid with a guide"),
2175                     INKSCAPE_ICON_SNAP_GRID_GUIDE_INTERSECTIONS, secondarySize,
2176                     SP_ATTR_INKSCAPE_SNAP_INTERS_GRIDGUIDE);
2178                 gtk_action_group_add_action( mainActions->gobj(), GTK_ACTION( act ) );
2179                 g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_snap_callback), toolbox );
2180         }*/
2182     GtkUIManager* mgr = gtk_ui_manager_new();
2183     GError* errVal = 0;
2185     gtk_ui_manager_insert_action_group( mgr, mainActions->gobj(), 0 );
2186     gtk_ui_manager_add_ui_from_string( mgr, descr, -1, &errVal );
2188     GtkWidget* toolBar = gtk_ui_manager_get_widget( mgr, "/ui/SnapToolbar" );
2189     if ( prefs->getBool("/toolbox/icononly", true) ) {
2190         gtk_toolbar_set_style( GTK_TOOLBAR(toolBar), GTK_TOOLBAR_ICONS );
2191     }
2193     Inkscape::IconSize toolboxSize = prefToSize("/toolbox/secondary");
2194     gtk_toolbar_set_icon_size( GTK_TOOLBAR(toolBar), static_cast<GtkIconSize>(toolboxSize) );
2196     gtk_toolbar_set_orientation(GTK_TOOLBAR(toolBar), GTK_ORIENTATION_HORIZONTAL);
2197     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolBar), TRUE);
2199     g_object_set_data(G_OBJECT(toolBar), "desktop", NULL);
2201     GtkWidget* child = gtk_bin_get_child(GTK_BIN(toolbox));
2202     if ( child ) {
2203         gtk_container_remove( GTK_CONTAINER(toolbox), child );
2204     }
2206     gtk_container_add( GTK_CONTAINER(toolbox), toolBar );
2210 void update_snap_toolbox(SPDesktop *desktop, SPEventContext */*eventcontext*/, GtkWidget *toolbox)
2212         g_assert(desktop != NULL);
2213         g_assert(toolbox != NULL);
2215         SPNamedView *nv = sp_desktop_namedview(desktop);
2216         if (nv == NULL) {
2217                 g_warning("Namedview cannot be retrieved (in update_snap_toolbox)!");
2218                 return;
2219         }
2221         Glib::RefPtr<Gtk::ActionGroup> mainActions = create_or_fetch_actions(desktop);
2223         Glib::RefPtr<Gtk::Action> act1 = mainActions->get_action("ToggleSnapGlobal");
2224         Glib::RefPtr<Gtk::Action> act2 = mainActions->get_action("ToggleSnapFromBBoxCorner");
2225         Glib::RefPtr<Gtk::Action> act3 = mainActions->get_action("ToggleSnapToBBoxPath");
2226         Glib::RefPtr<Gtk::Action> act4 = mainActions->get_action("ToggleSnapToBBoxNode");
2227         Glib::RefPtr<Gtk::Action> act4b = mainActions->get_action("ToggleSnapToFromBBoxEdgeMidpoints");
2228         Glib::RefPtr<Gtk::Action> act4c = mainActions->get_action("ToggleSnapToFromBBoxCenters");
2229         Glib::RefPtr<Gtk::Action> act5 = mainActions->get_action("ToggleSnapFromNode");
2230         Glib::RefPtr<Gtk::Action> act6 = mainActions->get_action("ToggleSnapToItemPath");
2231         Glib::RefPtr<Gtk::Action> act6b = mainActions->get_action("ToggleSnapToPathIntersections");
2232         Glib::RefPtr<Gtk::Action> act7 = mainActions->get_action("ToggleSnapToItemNode");
2233         Glib::RefPtr<Gtk::Action> act8 = mainActions->get_action("ToggleSnapToSmoothNodes");
2234         Glib::RefPtr<Gtk::Action> act9 = mainActions->get_action("ToggleSnapToFromLineMidpoints");
2235         Glib::RefPtr<Gtk::Action> act10 = mainActions->get_action("ToggleSnapToFromObjectCenters");
2236         Glib::RefPtr<Gtk::Action> act11 = mainActions->get_action("ToggleSnapToFromRotationCenter");
2237         Glib::RefPtr<Gtk::Action> act12 = mainActions->get_action("ToggleSnapToPageBorder");
2238         //Glib::RefPtr<Gtk::Action> act13 = mainActions->get_action("ToggleSnapToGridGuideIntersections");
2239         Glib::RefPtr<Gtk::Action> act14 = mainActions->get_action("ToggleSnapToGrids");
2240         Glib::RefPtr<Gtk::Action> act15 = mainActions->get_action("ToggleSnapToGuides");
2243         if (!act1) {
2244                 return; // The snap actions haven't been defined yet (might be the case during startup)
2245         }
2247         // The ..._set_active calls below will toggle the buttons, but this shouldn't lead to
2248         // changes in our document because we're only updating the UI;
2249         // Setting the "freeze" parameter to true will block the code in toggle_snap_callback()
2250         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(TRUE));
2252         bool const c1 = nv->snap_manager.snapprefs.getSnapEnabledGlobally();
2253         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act1->gobj()), c1);
2255         bool const c2 = nv->snap_manager.snapprefs.getSnapModeBBox();
2256         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act2->gobj()), c2);
2257         gtk_action_set_sensitive(GTK_ACTION(act2->gobj()), c1);
2259         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act3->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxPath());
2260         gtk_action_set_sensitive(GTK_ACTION(act3->gobj()), c1 && c2);
2261         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4->gobj()), nv->snap_manager.snapprefs.getSnapToBBoxNode());
2262         gtk_action_set_sensitive(GTK_ACTION(act4->gobj()), c1 && c2);
2263         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4b->gobj()), nv->snap_manager.snapprefs.getSnapBBoxEdgeMidpoints());
2264         gtk_action_set_sensitive(GTK_ACTION(act4b->gobj()), c1 && c2);
2265         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act4c->gobj()), nv->snap_manager.snapprefs.getSnapBBoxMidpoints());
2266         gtk_action_set_sensitive(GTK_ACTION(act4c->gobj()), c1 && c2);
2268         bool const c3 = nv->snap_manager.snapprefs.getSnapModeNode();
2269         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act5->gobj()), c3);
2270         gtk_action_set_sensitive(GTK_ACTION(act5->gobj()), c1);
2272         bool const c4 = nv->snap_manager.snapprefs.getSnapToItemPath();
2273         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6->gobj()), c4);
2274         gtk_action_set_sensitive(GTK_ACTION(act6->gobj()), c1 && c3);
2275         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act6b->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionCS());
2276         gtk_action_set_sensitive(GTK_ACTION(act6b->gobj()), c1 && c3 && c4);
2277         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act7->gobj()), nv->snap_manager.snapprefs.getSnapToItemNode());
2278         gtk_action_set_sensitive(GTK_ACTION(act7->gobj()), c1 && c3);
2279         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act8->gobj()), nv->snap_manager.snapprefs.getSnapSmoothNodes());
2280         gtk_action_set_sensitive(GTK_ACTION(act8->gobj()), c1 && c3);
2281         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act9->gobj()), nv->snap_manager.snapprefs.getSnapLineMidpoints());
2282         gtk_action_set_sensitive(GTK_ACTION(act9->gobj()), c1 && c3);
2283         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act10->gobj()), nv->snap_manager.snapprefs.getSnapObjectMidpoints());
2284         gtk_action_set_sensitive(GTK_ACTION(act10->gobj()), c1 && c3);
2285         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act11->gobj()), nv->snap_manager.snapprefs.getIncludeItemCenter());
2286         gtk_action_set_sensitive(GTK_ACTION(act11->gobj()), c1 && c3);
2288         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act12->gobj()), nv->snap_manager.snapprefs.getSnapToPageBorder());
2289         gtk_action_set_sensitive(GTK_ACTION(act12->gobj()), c1);
2290         //gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act13->gobj()), nv->snap_manager.snapprefs.getSnapIntersectionGG());
2291         //gtk_action_set_sensitive(GTK_ACTION(act13->gobj()), c1);
2293         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act14->gobj()), nv->snap_manager.snapprefs.getSnapToGrids());
2294         gtk_action_set_sensitive(GTK_ACTION(act14->gobj()), c1);
2295         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act15->gobj()), nv->snap_manager.snapprefs.getSnapToGuides());
2296         gtk_action_set_sensitive(GTK_ACTION(act15->gobj()), c1);
2299         g_object_set_data(G_OBJECT(toolbox), "freeze", GINT_TO_POINTER(FALSE)); // unfreeze (see above)
2302 void show_aux_toolbox(GtkWidget *toolbox_toplevel)
2304     gtk_widget_show(toolbox_toplevel);
2305     GtkWidget *toolbox = gtk_bin_get_child(GTK_BIN(toolbox_toplevel));
2307     GtkWidget *shown_toolbox = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "shows"));
2308     if (!shown_toolbox) {
2309         return;
2310     }
2311     gtk_widget_show(toolbox);
2313     gtk_widget_show_all(shown_toolbox);
2316 static GtkWidget *
2317 sp_empty_toolbox_new(SPDesktop *desktop)
2319     GtkWidget *tbl = gtk_toolbar_new();
2320     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
2321     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
2323     gtk_widget_show_all(tbl);
2324     sp_set_font_size_smaller (tbl);
2326     return tbl;
2329 #define MODE_LABEL_WIDTH 70
2331 //########################
2332 //##       Star         ##
2333 //########################
2335 static void sp_stb_magnitude_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2337     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2339     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2340         // do not remember prefs if this call is initiated by an undo change, because undoing object
2341         // creation sets bogus values to its attributes before it is deleted
2342         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2343         prefs->setInt("/tools/shapes/star/magnitude", (gint)adj->value);
2344     }
2346     // quit if run by the attr_changed listener
2347     if (g_object_get_data( dataKludge, "freeze" )) {
2348         return;
2349     }
2351     // in turn, prevent listener from responding
2352     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2354     bool modmade = false;
2356     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2357     GSList const *items = selection->itemList();
2358     for (; items != NULL; items = items->next) {
2359         if (SP_IS_STAR((SPItem *) items->data)) {
2360             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2361             sp_repr_set_int(repr,"sodipodi:sides",(gint)adj->value);
2362             sp_repr_set_svg_double(repr, "sodipodi:arg2",
2363                                    (sp_repr_get_double_attribute(repr, "sodipodi:arg1", 0.5)
2364                                     + M_PI / (gint)adj->value));
2365             SP_OBJECT((SPItem *) items->data)->updateRepr();
2366             modmade = true;
2367         }
2368     }
2369     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2370                                    _("Star: Change number of corners"));
2372     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2375 static void sp_stb_proportion_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2377     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2379     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2380         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2381         prefs->setDouble("/tools/shapes/star/proportion", adj->value);
2382     }
2384     // quit if run by the attr_changed listener
2385     if (g_object_get_data( dataKludge, "freeze" )) {
2386         return;
2387     }
2389     // in turn, prevent listener from responding
2390     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2392     bool modmade = false;
2393     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2394     GSList const *items = selection->itemList();
2395     for (; items != NULL; items = items->next) {
2396         if (SP_IS_STAR((SPItem *) items->data)) {
2397             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2399             gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2400             gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2401             if (r2 < r1) {
2402                 sp_repr_set_svg_double(repr, "sodipodi:r2", r1*adj->value);
2403             } else {
2404                 sp_repr_set_svg_double(repr, "sodipodi:r1", r2*adj->value);
2405             }
2407             SP_OBJECT((SPItem *) items->data)->updateRepr();
2408             modmade = true;
2409         }
2410     }
2412     if (modmade) sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2413                                    _("Star: Change spoke ratio"));
2415     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2418 static void sp_stb_sides_flat_state_changed( EgeSelectOneAction *act, GObject *dataKludge )
2420     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2421     bool flat = ege_select_one_action_get_active( act ) == 0;
2423     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2424         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2425         prefs->setBool( "/tools/shapes/star/isflatsided", flat);
2426     }
2428     // quit if run by the attr_changed listener
2429     if (g_object_get_data( dataKludge, "freeze" )) {
2430         return;
2431     }
2433     // in turn, prevent listener from responding
2434     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2436     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2437     GSList const *items = selection->itemList();
2438     GtkAction* prop_action = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2439     bool modmade = false;
2441     if ( prop_action ) {
2442         gtk_action_set_sensitive( prop_action, !flat );
2443     }
2445     for (; items != NULL; items = items->next) {
2446         if (SP_IS_STAR((SPItem *) items->data)) {
2447             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2448             repr->setAttribute("inkscape:flatsided", flat ? "true" : "false" );
2449             SP_OBJECT((SPItem *) items->data)->updateRepr();
2450             modmade = true;
2451         }
2452     }
2454     if (modmade) {
2455         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2456                          flat ? _("Make polygon") : _("Make star"));
2457     }
2459     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2462 static void sp_stb_rounded_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2464     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2466     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2467         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2468         prefs->setDouble("/tools/shapes/star/rounded", (gdouble) adj->value);
2469     }
2471     // quit if run by the attr_changed listener
2472     if (g_object_get_data( dataKludge, "freeze" )) {
2473         return;
2474     }
2476     // in turn, prevent listener from responding
2477     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2479     bool modmade = false;
2481     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2482     GSList const *items = selection->itemList();
2483     for (; items != NULL; items = items->next) {
2484         if (SP_IS_STAR((SPItem *) items->data)) {
2485             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2486             sp_repr_set_svg_double(repr, "inkscape:rounded", (gdouble) adj->value);
2487             SP_OBJECT(items->data)->updateRepr();
2488             modmade = true;
2489         }
2490     }
2491     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2492                                    _("Star: Change rounding"));
2494     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2497 static void sp_stb_randomized_value_changed( GtkAdjustment *adj, GObject *dataKludge )
2499     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
2501     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2502         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2503         prefs->setDouble("/tools/shapes/star/randomized", (gdouble) adj->value);
2504     }
2506     // quit if run by the attr_changed listener
2507     if (g_object_get_data( dataKludge, "freeze" )) {
2508         return;
2509     }
2511     // in turn, prevent listener from responding
2512     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(TRUE) );
2514     bool modmade = false;
2516     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2517     GSList const *items = selection->itemList();
2518     for (; items != NULL; items = items->next) {
2519         if (SP_IS_STAR((SPItem *) items->data)) {
2520             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
2521             sp_repr_set_svg_double(repr, "inkscape:randomized", (gdouble) adj->value);
2522             SP_OBJECT(items->data)->updateRepr();
2523             modmade = true;
2524         }
2525     }
2526     if (modmade)  sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_STAR,
2527                                    _("Star: Change randomization"));
2529     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
2533 static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
2534                                        gchar const */*old_value*/, gchar const */*new_value*/,
2535                                        bool /*is_interactive*/, gpointer data)
2537     GtkWidget *tbl = GTK_WIDGET(data);
2539     // quit if run by the _changed callbacks
2540     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
2541         return;
2542     }
2544     // in turn, prevent callbacks from responding
2545     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
2547     GtkAdjustment *adj = 0;
2549     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2550     bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2552     if (!strcmp(name, "inkscape:randomized")) {
2553         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
2554         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
2555     } else if (!strcmp(name, "inkscape:rounded")) {
2556         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "rounded") );
2557         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:rounded", 0.0));
2558     } else if (!strcmp(name, "inkscape:flatsided")) {
2559         GtkAction* prop_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "prop_action") );
2560         char const *flatsides = repr->attribute("inkscape:flatsided");
2561         EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( G_OBJECT(tbl), "flat_action" ) );
2562         if ( flatsides && !strcmp(flatsides,"false") ) {
2563             ege_select_one_action_set_active( flat_action, 1 );
2564             gtk_action_set_sensitive( prop_action, TRUE );
2565         } else {
2566             ege_select_one_action_set_active( flat_action, 0 );
2567             gtk_action_set_sensitive( prop_action, FALSE );
2568         }
2569     } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
2570         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
2571         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
2572         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
2573         if (r2 < r1) {
2574             gtk_adjustment_set_value(adj, r2/r1);
2575         } else {
2576             gtk_adjustment_set_value(adj, r1/r2);
2577         }
2578     } else if (!strcmp(name, "sodipodi:sides")) {
2579         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "magnitude");
2580         gtk_adjustment_set_value(adj, sp_repr_get_int_attribute(repr, "sodipodi:sides", 0));
2581     }
2583     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
2587 static Inkscape::XML::NodeEventVector star_tb_repr_events =
2589     NULL, /* child_added */
2590     NULL, /* child_removed */
2591     star_tb_event_attr_changed,
2592     NULL, /* content_changed */
2593     NULL  /* order_changed */
2594 };
2597 /**
2598  *  \param selection Should not be NULL.
2599  */
2600 static void
2601 sp_star_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
2603     int n_selected = 0;
2604     Inkscape::XML::Node *repr = NULL;
2606     purge_repr_listener( tbl, tbl );
2608     for (GSList const *items = selection->itemList();
2609          items != NULL;
2610          items = items->next)
2611     {
2612         if (SP_IS_STAR((SPItem *) items->data)) {
2613             n_selected++;
2614             repr = SP_OBJECT_REPR((SPItem *) items->data);
2615         }
2616     }
2618     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
2620     if (n_selected == 0) {
2621         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
2622     } else if (n_selected == 1) {
2623         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
2625         if (repr) {
2626             g_object_set_data( tbl, "repr", repr );
2627             Inkscape::GC::anchor(repr);
2628             sp_repr_add_listener(repr, &star_tb_repr_events, tbl);
2629             sp_repr_synthesize_events(repr, &star_tb_repr_events, tbl);
2630         }
2631     } else {
2632         // FIXME: implement averaging of all parameters for multiple selected stars
2633         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
2634         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Change:</b>"));
2635     }
2639 static void sp_stb_defaults( GtkWidget */*widget*/, GObject *dataKludge )
2641     // FIXME: in this and all other _default functions, set some flag telling the value_changed
2642     // callbacks to lump all the changes for all selected objects in one undo step
2644     GtkAdjustment *adj = 0;
2646     // fixme: make settable in prefs!
2647     gint mag = 5;
2648     gdouble prop = 0.5;
2649     gboolean flat = FALSE;
2650     gdouble randomized = 0;
2651     gdouble rounded = 0;
2653     EgeSelectOneAction* flat_action = EGE_SELECT_ONE_ACTION( g_object_get_data( dataKludge, "flat_action" ) );
2654     ege_select_one_action_set_active( flat_action, flat ? 0 : 1 );
2656     GtkAction* sb2 = GTK_ACTION( g_object_get_data( dataKludge, "prop_action" ) );
2657     gtk_action_set_sensitive( sb2, !flat );
2659     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "magnitude" ) );
2660     gtk_adjustment_set_value(adj, mag);
2661     gtk_adjustment_value_changed(adj);
2663     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "proportion" ) );
2664     gtk_adjustment_set_value(adj, prop);
2665     gtk_adjustment_value_changed(adj);
2667     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "rounded" ) );
2668     gtk_adjustment_set_value(adj, rounded);
2669     gtk_adjustment_value_changed(adj);
2671     adj = GTK_ADJUSTMENT( g_object_get_data( dataKludge, "randomized" ) );
2672     gtk_adjustment_set_value(adj, randomized);
2673     gtk_adjustment_value_changed(adj);
2677 void
2678 sp_toolbox_add_label(GtkWidget *tbl, gchar const *title, bool wide)
2680     GtkWidget *boxl = gtk_hbox_new(FALSE, 0);
2681     if (wide) gtk_widget_set_size_request(boxl, MODE_LABEL_WIDTH, -1);
2682     GtkWidget *l = gtk_label_new(NULL);
2683     gtk_label_set_markup(GTK_LABEL(l), title);
2684     gtk_box_pack_end(GTK_BOX(boxl), l, FALSE, FALSE, 0);
2685     if ( GTK_IS_TOOLBAR(tbl) ) {
2686         gtk_toolbar_append_widget( GTK_TOOLBAR(tbl), boxl, "", "" );
2687     } else {
2688         gtk_box_pack_start(GTK_BOX(tbl), boxl, FALSE, FALSE, 0);
2689     }
2690     gtk_object_set_data(GTK_OBJECT(tbl), "mode_label", l);
2694 static void sp_star_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
2696     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
2698     {
2699         EgeOutputAction* act = ege_output_action_new( "StarStateAction", _("<b>New:</b>"), "", 0 );
2700         ege_output_action_set_use_markup( act, TRUE );
2701         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
2702         g_object_set_data( holder, "mode_action", act );
2703     }
2705     {
2706         EgeAdjustmentAction* eact = 0;
2707         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2708         bool isFlatSided = prefs->getBool("/tools/shapes/star/isflatsided", true);
2710         /* Flatsided checkbox */
2711         {
2712             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
2714             GtkTreeIter iter;
2715             gtk_list_store_append( model, &iter );
2716             gtk_list_store_set( model, &iter,
2717                                 0, _("Polygon"),
2718                                 1, _("Regular polygon (with one handle) instead of a star"),
2719                                 2, INKSCAPE_ICON_DRAW_POLYGON,
2720                                 -1 );
2722             gtk_list_store_append( model, &iter );
2723             gtk_list_store_set( model, &iter,
2724                                 0, _("Star"),
2725                                 1, _("Star instead of a regular polygon (with one handle)"),
2726                                 2, INKSCAPE_ICON_DRAW_STAR,
2727                                 -1 );
2729             EgeSelectOneAction* act = ege_select_one_action_new( "FlatAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
2730             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
2731             g_object_set_data( holder, "flat_action", act );
2733             ege_select_one_action_set_appearance( act, "full" );
2734             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
2735             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
2736             ege_select_one_action_set_icon_column( act, 2 );
2737             ege_select_one_action_set_icon_size( act, secondarySize );
2738             ege_select_one_action_set_tooltip_column( act, 1  );
2740             ege_select_one_action_set_active( act, isFlatSided ? 0 : 1 );
2741             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_stb_sides_flat_state_changed), holder);
2742         }
2744         /* Magnitude */
2745         {
2746         gchar const* labels[] = {_("triangle/tri-star"), _("square/quad-star"), _("pentagon/five-pointed star"), _("hexagon/six-pointed star"), 0, 0, 0, 0, 0};
2747         gdouble values[] = {3, 4, 5, 6, 7, 8, 10, 12, 20};
2748         eact = create_adjustment_action( "MagnitudeAction",
2749                                          _("Corners"), _("Corners:"), _("Number of corners of a polygon or star"),
2750                                          "/tools/shapes/star/magnitude", 3,
2751                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2752                                          3, 1024, 1, 5,
2753                                          labels, values, G_N_ELEMENTS(labels),
2754                                          sp_stb_magnitude_value_changed,
2755                                          1.0, 0 );
2756         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2757         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2758         }
2760         /* Spoke ratio */
2761         {
2762         gchar const* labels[] = {_("thin-ray star"), 0, _("pentagram"), _("hexagram"), _("heptagram"), _("octagram"), _("regular polygon")};
2763         gdouble values[] = {0.01, 0.2, 0.382, 0.577, 0.692, 0.765, 1};
2764         eact = create_adjustment_action( "SpokeAction",
2765                                          _("Spoke ratio"), _("Spoke ratio:"),
2766                                          // TRANSLATORS: Tip radius of a star is the distance from the center to the farthest handle.
2767                                          // Base radius is the same for the closest handle.
2768                                          _("Base radius to tip radius ratio"),
2769                                          "/tools/shapes/star/proportion", 0.5,
2770                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2771                                          0.01, 1.0, 0.01, 0.1,
2772                                          labels, values, G_N_ELEMENTS(labels),
2773                                          sp_stb_proportion_value_changed );
2774         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2775         g_object_set_data( holder, "prop_action", eact );
2776         }
2778         if ( !isFlatSided ) {
2779             gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2780         } else {
2781             gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
2782         }
2784         /* Roundedness */
2785         {
2786         gchar const* labels[] = {_("stretched"), _("twisted"), _("slightly pinched"), _("NOT rounded"), _("slightly rounded"), _("visibly rounded"), _("well rounded"), _("amply rounded"), 0, _("stretched"), _("blown up")};
2787         gdouble values[] = {-1, -0.2, -0.03, 0, 0.05, 0.1, 0.2, 0.3, 0.5, 1, 10};
2788         eact = create_adjustment_action( "RoundednessAction",
2789                                          _("Rounded"), _("Rounded:"), _("How much rounded are the corners (0 for sharp)"),
2790                                          "/tools/shapes/star/rounded", 0.0,
2791                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2792                                          -10.0, 10.0, 0.01, 0.1,
2793                                          labels, values, G_N_ELEMENTS(labels),
2794                                          sp_stb_rounded_value_changed );
2795         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2796         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2797         }
2799         /* Randomization */
2800         {
2801         gchar const* labels[] = {_("NOT randomized"), _("slightly irregular"), _("visibly randomized"), _("strongly randomized"), _("blown up")};
2802         gdouble values[] = {0, 0.01, 0.1, 0.5, 10};
2803         eact = create_adjustment_action( "RandomizationAction",
2804                                          _("Randomized"), _("Randomized:"), _("Scatter randomly the corners and angles"),
2805                                          "/tools/shapes/star/randomized", 0.0,
2806                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
2807                                          -10.0, 10.0, 0.001, 0.01,
2808                                          labels, values, G_N_ELEMENTS(labels),
2809                                          sp_stb_randomized_value_changed, 0.1, 3 );
2810         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
2811         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
2812         }
2813     }
2815     {
2816         /* Reset */
2817         {
2818             GtkAction* act = gtk_action_new( "StarResetAction",
2819                                              _("Defaults"),
2820                                              _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
2821                                              GTK_STOCK_CLEAR );
2822             g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(sp_stb_defaults), holder );
2823             gtk_action_group_add_action( mainActions, act );
2824             gtk_action_set_sensitive( act, TRUE );
2825         }
2826     }
2828     sigc::connection *connection = new sigc::connection(
2829         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_star_toolbox_selection_changed), (GObject *)holder))
2830         );
2831     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
2832     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
2836 //########################
2837 //##       Rect         ##
2838 //########################
2840 static void sp_rtb_sensitivize( GObject *tbl )
2842     GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data(tbl, "rx") );
2843     GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data(tbl, "ry") );
2844     GtkAction* not_rounded = GTK_ACTION( g_object_get_data(tbl, "not_rounded") );
2846     if (adj1->value == 0 && adj2->value == 0 && g_object_get_data(tbl, "single")) { // only for a single selected rect (for now)
2847         gtk_action_set_sensitive( not_rounded, FALSE );
2848     } else {
2849         gtk_action_set_sensitive( not_rounded, TRUE );
2850     }
2854 static void
2855 sp_rtb_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name,
2856                           void (*setter)(SPRect *, gdouble))
2858     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
2860     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
2861     SPUnit const *unit = tracker->getActiveUnit();
2863     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
2864         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
2865         prefs->setDouble(Glib::ustring("/tools/shapes/rect/") + value_name, sp_units_get_pixels(adj->value, *unit));
2866     }
2868     // quit if run by the attr_changed listener
2869     if (g_object_get_data( tbl, "freeze" )) {
2870         return;
2871     }
2873     // in turn, prevent listener from responding
2874     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE));
2876     bool modmade = false;
2877     Inkscape::Selection *selection = sp_desktop_selection(desktop);
2878     for (GSList const *items = selection->itemList(); items != NULL; items = items->next) {
2879         if (SP_IS_RECT(items->data)) {
2880             if (adj->value != 0) {
2881                 setter(SP_RECT(items->data), sp_units_get_pixels(adj->value, *unit));
2882             } else {
2883                 SP_OBJECT_REPR(items->data)->setAttribute(value_name, NULL);
2884             }
2885             modmade = true;
2886         }
2887     }
2889     sp_rtb_sensitivize( tbl );
2891     if (modmade) {
2892         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_RECT,
2893                                    _("Change rectangle"));
2894     }
2896     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2899 static void
2900 sp_rtb_rx_value_changed(GtkAdjustment *adj, GObject *tbl)
2902     sp_rtb_value_changed(adj, tbl, "rx", sp_rect_set_visible_rx);
2905 static void
2906 sp_rtb_ry_value_changed(GtkAdjustment *adj, GObject *tbl)
2908     sp_rtb_value_changed(adj, tbl, "ry", sp_rect_set_visible_ry);
2911 static void
2912 sp_rtb_width_value_changed(GtkAdjustment *adj, GObject *tbl)
2914     sp_rtb_value_changed(adj, tbl, "width", sp_rect_set_visible_width);
2917 static void
2918 sp_rtb_height_value_changed(GtkAdjustment *adj, GObject *tbl)
2920     sp_rtb_value_changed(adj, tbl, "height", sp_rect_set_visible_height);
2925 static void
2926 sp_rtb_defaults( GtkWidget */*widget*/, GObject *obj)
2928     GtkAdjustment *adj = 0;
2930     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "rx") );
2931     gtk_adjustment_set_value(adj, 0.0);
2932     // this is necessary if the previous value was 0, but we still need to run the callback to change all selected objects
2933     gtk_adjustment_value_changed(adj);
2935     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "ry") );
2936     gtk_adjustment_set_value(adj, 0.0);
2937     gtk_adjustment_value_changed(adj);
2939     sp_rtb_sensitivize( obj );
2942 static void rect_tb_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const */*name*/,
2943                                        gchar const */*old_value*/, gchar const */*new_value*/,
2944                                        bool /*is_interactive*/, gpointer data)
2946     GObject *tbl = G_OBJECT(data);
2948     // quit if run by the _changed callbacks
2949     if (g_object_get_data( tbl, "freeze" )) {
2950         return;
2951     }
2953     // in turn, prevent callbacks from responding
2954     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
2956     UnitTracker* tracker = reinterpret_cast<UnitTracker*>( g_object_get_data( tbl, "tracker" ) );
2957     SPUnit const *unit = tracker->getActiveUnit();
2959     gpointer item = g_object_get_data( tbl, "item" );
2960     if (item && SP_IS_RECT(item)) {
2961         {
2962             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "rx" ) );
2963             gdouble rx = sp_rect_get_visible_rx(SP_RECT(item));
2964             gtk_adjustment_set_value(adj, sp_pixels_get_units(rx, *unit));
2965         }
2967         {
2968             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "ry" ) );
2969             gdouble ry = sp_rect_get_visible_ry(SP_RECT(item));
2970             gtk_adjustment_set_value(adj, sp_pixels_get_units(ry, *unit));
2971         }
2973         {
2974             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "width" ) );
2975             gdouble width = sp_rect_get_visible_width (SP_RECT(item));
2976             gtk_adjustment_set_value(adj, sp_pixels_get_units(width, *unit));
2977         }
2979         {
2980             GtkAdjustment *adj = GTK_ADJUSTMENT( g_object_get_data( tbl, "height" ) );
2981             gdouble height = sp_rect_get_visible_height (SP_RECT(item));
2982             gtk_adjustment_set_value(adj, sp_pixels_get_units(height, *unit));
2983         }
2984     }
2986     sp_rtb_sensitivize( tbl );
2988     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
2992 static Inkscape::XML::NodeEventVector rect_tb_repr_events = {
2993     NULL, /* child_added */
2994     NULL, /* child_removed */
2995     rect_tb_event_attr_changed,
2996     NULL, /* content_changed */
2997     NULL  /* order_changed */
2998 };
3000 /**
3001  *  \param selection should not be NULL.
3002  */
3003 static void
3004 sp_rect_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3006     int n_selected = 0;
3007     Inkscape::XML::Node *repr = NULL;
3008     SPItem *item = NULL;
3010     if ( g_object_get_data( tbl, "repr" ) ) {
3011         g_object_set_data( tbl, "item", NULL );
3012     }
3013     purge_repr_listener( tbl, tbl );
3015     for (GSList const *items = selection->itemList();
3016          items != NULL;
3017          items = items->next) {
3018         if (SP_IS_RECT((SPItem *) items->data)) {
3019             n_selected++;
3020             item = (SPItem *) items->data;
3021             repr = SP_OBJECT_REPR(item);
3022         }
3023     }
3025     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3027     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
3029     if (n_selected == 0) {
3030         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3032         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3033         gtk_action_set_sensitive(w, FALSE);
3034         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3035         gtk_action_set_sensitive(h, FALSE);
3037     } else if (n_selected == 1) {
3038         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3039         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
3041         GtkAction* w = GTK_ACTION( g_object_get_data( tbl, "width_action" ) );
3042         gtk_action_set_sensitive(w, TRUE);
3043         GtkAction* h = GTK_ACTION( g_object_get_data( tbl, "height_action" ) );
3044         gtk_action_set_sensitive(h, TRUE);
3046         if (repr) {
3047             g_object_set_data( tbl, "repr", repr );
3048             g_object_set_data( tbl, "item", item );
3049             Inkscape::GC::anchor(repr);
3050             sp_repr_add_listener(repr, &rect_tb_repr_events, tbl);
3051             sp_repr_synthesize_events(repr, &rect_tb_repr_events, tbl);
3052         }
3053     } else {
3054         // FIXME: implement averaging of all parameters for multiple selected
3055         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3056         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3057         sp_rtb_sensitivize( tbl );
3058     }
3062 static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3064     EgeAdjustmentAction* eact = 0;
3065     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3067     {
3068         EgeOutputAction* act = ege_output_action_new( "RectStateAction", _("<b>New:</b>"), "", 0 );
3069         ege_output_action_set_use_markup( act, TRUE );
3070         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3071         g_object_set_data( holder, "mode_action", act );
3072     }
3074     // rx/ry units menu: create
3075     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
3076     //tracker->addUnit( SP_UNIT_PERCENT, 0 );
3077     // fixme: add % meaning per cent of the width/height
3078     tracker->setActiveUnit( sp_desktop_namedview(desktop)->doc_units );
3079     g_object_set_data( holder, "tracker", tracker );
3081     /* W */
3082     {
3083         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3084         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3085         eact = create_adjustment_action( "RectWidthAction",
3086                                          _("Width"), _("W:"), _("Width of rectangle"),
3087                                          "/tools/shapes/rect/width", 0,
3088                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-rect",
3089                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3090                                          labels, values, G_N_ELEMENTS(labels),
3091                                          sp_rtb_width_value_changed );
3092         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3093         g_object_set_data( holder, "width_action", eact );
3094         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3095         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3096     }
3098     /* H */
3099     {
3100         gchar const* labels[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
3101         gdouble values[] = {1, 2, 3, 5, 10, 20, 50, 100, 200, 500};
3102         eact = create_adjustment_action( "RectHeightAction",
3103                                          _("Height"), _("H:"), _("Height of rectangle"),
3104                                          "/tools/shapes/rect/height", 0,
3105                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3106                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3107                                          labels, values, G_N_ELEMENTS(labels),
3108                                          sp_rtb_height_value_changed );
3109         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3110         g_object_set_data( holder, "height_action", eact );
3111         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3112         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3113     }
3115     /* rx */
3116     {
3117         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3118         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3119         eact = create_adjustment_action( "RadiusXAction",
3120                                          _("Horizontal radius"), _("Rx:"), _("Horizontal radius of rounded corners"),
3121                                          "/tools/shapes/rect/rx", 0,
3122                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3123                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3124                                          labels, values, G_N_ELEMENTS(labels),
3125                                          sp_rtb_rx_value_changed);
3126         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3127         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3128     }
3130     /* ry */
3131     {
3132         gchar const* labels[] = {_("not rounded"), 0, 0, 0, 0, 0, 0, 0, 0};
3133         gdouble values[] = {0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3134         eact = create_adjustment_action( "RadiusYAction",
3135                                          _("Vertical radius"), _("Ry:"), _("Vertical radius of rounded corners"),
3136                                          "/tools/shapes/rect/ry", 0,
3137                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
3138                                          0, 1e6, SPIN_STEP, SPIN_PAGE_STEP,
3139                                          labels, values, G_N_ELEMENTS(labels),
3140                                          sp_rtb_ry_value_changed);
3141         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
3142         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3143     }
3145     // add the units menu
3146     {
3147         GtkAction* act = tracker->createAction( "RectUnitsAction", _("Units"), ("") );
3148         gtk_action_group_add_action( mainActions, act );
3149     }
3151     /* Reset */
3152     {
3153         InkAction* inky = ink_action_new( "RectResetAction",
3154                                           _("Not rounded"),
3155                                           _("Make corners sharp"),
3156                                           INKSCAPE_ICON_RECTANGLE_MAKE_CORNERS_SHARP,
3157                                           secondarySize );
3158         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_rtb_defaults), holder );
3159         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3160         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
3161         g_object_set_data( holder, "not_rounded", inky );
3162     }
3164     g_object_set_data( holder, "single", GINT_TO_POINTER(TRUE) );
3165     sp_rtb_sensitivize( holder );
3167     sigc::connection *connection = new sigc::connection(
3168         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_rect_toolbox_selection_changed), (GObject *)holder))
3169         );
3170     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3171     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3174 //########################
3175 //##       3D Box       ##
3176 //########################
3178 // normalize angle so that it lies in the interval [0,360]
3179 static double box3d_normalize_angle (double a) {
3180     double angle = a + ((int) (a/360.0))*360;
3181     if (angle < 0) {
3182         angle += 360.0;
3183     }
3184     return angle;
3187 static void
3188 box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
3189                                 GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
3190     // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
3191     //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
3192     //       are reset).
3193     bool is_infinite = !persp3d_VP_is_finite(persp, axis);
3195     if (is_infinite) {
3196         gtk_toggle_action_set_active(tact, TRUE);
3197         gtk_action_set_sensitive(act, TRUE);
3199         double angle = persp3d_get_infinite_angle(persp, axis);
3200         if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
3201             gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
3202         }
3203     } else {
3204         gtk_toggle_action_set_active(tact, FALSE);
3205         gtk_action_set_sensitive(act, FALSE);
3206     }
3209 static void
3210 box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
3211     if (!persp_repr) {
3212         g_print ("No perspective given to box3d_resync_toolbar().\n");
3213         return;
3214     }
3216     GtkWidget *tbl = GTK_WIDGET(data);
3217     GtkAdjustment *adj = 0;
3218     GtkAction *act = 0;
3219     GtkToggleAction *tact = 0;
3220     Persp3D *persp = persp3d_get_from_repr(persp_repr);
3221     {
3222         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
3223         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
3224         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
3226         box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
3227     }
3228     {
3229         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
3230         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
3231         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
3233         box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
3234     }
3235     {
3236         adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
3237         act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
3238         tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
3240         box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
3241     }
3244 static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3245                                                   gchar const */*old_value*/, gchar const */*new_value*/,
3246                                                   bool /*is_interactive*/, gpointer data)
3248     GtkWidget *tbl = GTK_WIDGET(data);
3250     // quit if run by the attr_changed or selection changed listener
3251     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3252         return;
3253     }
3255     // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
3256     // sp_document_maybe_done() when the document is undo insensitive)
3257     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3259     // TODO: Only update the appropriate part of the toolbar
3260 //    if (!strcmp(name, "inkscape:vp_z")) {
3261         box3d_resync_toolbar(repr, G_OBJECT(tbl));
3262 //    }
3264     Persp3D *persp = persp3d_get_from_repr(repr);
3265     persp3d_update_box_reprs(persp);
3267     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3270 static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
3272     NULL, /* child_added */
3273     NULL, /* child_removed */
3274     box3d_persp_tb_event_attr_changed,
3275     NULL, /* content_changed */
3276     NULL  /* order_changed */
3277 };
3279 /**
3280  *  \param selection Should not be NULL.
3281  */
3282 // FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
3283 //        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
3284 static void
3285 box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3287     // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
3288     // disable the angle entry fields for this direction (otherwise entering a value in them should only
3289     // update the perspectives with infinite VPs and leave the other ones untouched).
3291     Inkscape::XML::Node *persp_repr = NULL;
3292     purge_repr_listener(tbl, tbl);
3294     SPItem *item = selection->singleItem();
3295     if (item && SP_IS_BOX3D(item)) {
3296         // FIXME: Also deal with multiple selected boxes
3297         SPBox3D *box = SP_BOX3D(item);
3298         Persp3D *persp = box3d_get_perspective(box);
3299         persp_repr = SP_OBJECT_REPR(persp);
3300         if (persp_repr) {
3301             g_object_set_data(tbl, "repr", persp_repr);
3302             Inkscape::GC::anchor(persp_repr);
3303             sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
3304             sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
3305         }
3307         inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
3308         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3309         prefs->setString("/tools/shapes/3dbox/persp", persp_repr->attribute("id"));
3311         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
3312         box3d_resync_toolbar(persp_repr, tbl);
3313         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
3314     }
3317 static void
3318 box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
3320     SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
3321     SPDocument *document = sp_desktop_document(desktop);
3323     // quit if run by the attr_changed or selection changed listener
3324     if (g_object_get_data( dataKludge, "freeze" )) {
3325         return;
3326     }
3328     // in turn, prevent listener from responding
3329     g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
3331     //Persp3D *persp = document->current_persp3d;
3332     std::list<Persp3D *> sel_persps = sp_desktop_selection(desktop)->perspList();
3333     if (sel_persps.empty()) {
3334         // this can happen when the document is created; we silently ignore it
3335         return;
3336     }
3337     Persp3D *persp = sel_persps.front();
3339     persp->tmat.set_infinite_direction (axis, adj->value);
3340     SP_OBJECT(persp)->updateRepr();
3342     // TODO: use the correct axis here, too
3343     sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
3345     g_object_set_data( dataKludge, "freeze", GINT_TO_POINTER(FALSE) );
3349 static void
3350 box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3352     box3d_angle_value_changed(adj, dataKludge, Proj::X);
3355 static void
3356 box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3358     box3d_angle_value_changed(adj, dataKludge, Proj::Y);
3361 static void
3362 box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
3364     box3d_angle_value_changed(adj, dataKludge, Proj::Z);
3368 static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction */*box3d_angle*/, Proj::Axis axis )
3370     // TODO: Take all selected perspectives into account
3371     std::list<Persp3D *> sel_persps = sp_desktop_selection(inkscape_active_desktop())->perspList();
3372     if (sel_persps.empty()) {
3373         // this can happen when the document is created; we silently ignore it
3374         return;
3375     }
3376     Persp3D *persp = sel_persps.front();
3378     bool set_infinite = gtk_toggle_action_get_active(act);
3379     persp3d_set_VP_state (persp, axis, set_infinite ? Proj::VP_INFINITE : Proj::VP_FINITE);
3382 static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3384     box3d_vp_state_changed(act, box3d_angle, Proj::X);
3387 static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3389     box3d_vp_state_changed(act, box3d_angle, Proj::Y);
3392 static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
3394     box3d_vp_state_changed(act, box3d_angle, Proj::Z);
3397 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3399     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3400     EgeAdjustmentAction* eact = 0;
3401     SPDocument *document = sp_desktop_document (desktop);
3402     Persp3D *persp = document->current_persp3d;
3404     EgeAdjustmentAction* box3d_angle_x = 0;
3405     EgeAdjustmentAction* box3d_angle_y = 0;
3406     EgeAdjustmentAction* box3d_angle_z = 0;
3408     /* Angle X */
3409     {
3410         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3411         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3412         eact = create_adjustment_action( "3DBoxAngleXAction",
3413                                          _("Angle in X direction"), _("Angle X:"),
3414                                          // Translators: PL is short for 'perspective line'
3415                                          _("Angle of PLs in X direction"),
3416                                          "/tools/shapes/3dbox/box3d_angle_x", 30,
3417                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
3418                                          -360.0, 360.0, 1.0, 10.0,
3419                                          labels, values, G_N_ELEMENTS(labels),
3420                                          box3d_angle_x_value_changed );
3421         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3422         g_object_set_data( holder, "box3d_angle_x_action", eact );
3423         box3d_angle_x = eact;
3424     }
3426     if (!persp3d_VP_is_finite(persp, Proj::X)) {
3427         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3428     } else {
3429         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3430     }
3433     /* VP X state */
3434     {
3435         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
3436                                                       // Translators: VP is short for 'vanishing point'
3437                                                       _("State of VP in X direction"),
3438                                                       _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
3439                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3440                                                       Inkscape::ICON_SIZE_DECORATION );
3441         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3442         g_object_set_data( holder, "box3d_vp_x_state_action", act );
3443         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
3444         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3445         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_x_state", true) );
3446     }
3448     /* Angle Y */
3449     {
3450         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3451         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3452         eact = create_adjustment_action( "3DBoxAngleYAction",
3453                                          _("Angle in Y direction"), _("Angle Y:"),
3454                                          // Translators: PL is short for 'perspective line'
3455                                          _("Angle of PLs in Y direction"),
3456                                          "/tools/shapes/3dbox/box3d_angle_y", 30,
3457                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3458                                          -360.0, 360.0, 1.0, 10.0,
3459                                          labels, values, G_N_ELEMENTS(labels),
3460                                          box3d_angle_y_value_changed );
3461         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3462         g_object_set_data( holder, "box3d_angle_y_action", eact );
3463         box3d_angle_y = eact;
3464     }
3466     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
3467         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3468     } else {
3469         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3470     }
3472     /* VP Y state */
3473     {
3474         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
3475                                                       // Translators: VP is short for 'vanishing point'
3476                                                       _("State of VP in Y direction"),
3477                                                       _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
3478                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3479                                                       Inkscape::ICON_SIZE_DECORATION );
3480         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3481         g_object_set_data( holder, "box3d_vp_y_state_action", act );
3482         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
3483         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3484         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_y_state", true) );
3485     }
3487     /* Angle Z */
3488     {
3489         gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
3490         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
3491         eact = create_adjustment_action( "3DBoxAngleZAction",
3492                                          _("Angle in Z direction"), _("Angle Z:"),
3493                                          // Translators: PL is short for 'perspective line'
3494                                          _("Angle of PLs in Z direction"),
3495                                          "/tools/shapes/3dbox/box3d_angle_z", 30,
3496                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3497                                          -360.0, 360.0, 1.0, 10.0,
3498                                          labels, values, G_N_ELEMENTS(labels),
3499                                          box3d_angle_z_value_changed );
3500         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3501         g_object_set_data( holder, "box3d_angle_z_action", eact );
3502         box3d_angle_z = eact;
3503     }
3505     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
3506         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
3507     } else {
3508         gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
3509     }
3511     /* VP Z state */
3512     {
3513         InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
3514                                                       // Translators: VP is short for 'vanishing point'
3515                                                       _("State of VP in Z direction"),
3516                                                       _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
3517                                                       INKSCAPE_ICON_PERSPECTIVE_PARALLEL,
3518                                                       Inkscape::ICON_SIZE_DECORATION );
3519         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3520         g_object_set_data( holder, "box3d_vp_z_state_action", act );
3521         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
3522         gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3523         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/shapes/3dbox/vp_z_state", true) );
3524     }
3526     sigc::connection *connection = new sigc::connection(
3527         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(box3d_toolbox_selection_changed), (GObject *)holder))
3528        );
3529     g_signal_connect(holder, "destroy", G_CALLBACK(delete_connection), connection);
3530     g_signal_connect(holder, "destroy", G_CALLBACK(purge_repr_listener), holder);
3533 //########################
3534 //##       Spiral       ##
3535 //########################
3537 static void
3538 sp_spl_tb_value_changed(GtkAdjustment *adj, GObject *tbl, Glib::ustring const &value_name)
3540     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
3542     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
3543         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3544         prefs->setDouble("/tools/shapes/spiral/" + value_name, adj->value);
3545     }
3547     // quit if run by the attr_changed listener
3548     if (g_object_get_data( tbl, "freeze" )) {
3549         return;
3550     }
3552     // in turn, prevent listener from responding
3553     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3555     gchar* namespaced_name = g_strconcat("sodipodi:", value_name.data(), NULL);
3557     bool modmade = false;
3558     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
3559          items != NULL;
3560          items = items->next)
3561     {
3562         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3563             Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
3564             sp_repr_set_svg_double( repr, namespaced_name, adj->value );
3565             SP_OBJECT((SPItem *) items->data)->updateRepr();
3566             modmade = true;
3567         }
3568     }
3570     g_free(namespaced_name);
3572     if (modmade) {
3573         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_SPIRAL,
3574                                    _("Change spiral"));
3575     }
3577     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3580 static void
3581 sp_spl_tb_revolution_value_changed(GtkAdjustment *adj, GObject *tbl)
3583     sp_spl_tb_value_changed(adj, tbl, "revolution");
3586 static void
3587 sp_spl_tb_expansion_value_changed(GtkAdjustment *adj, GObject *tbl)
3589     sp_spl_tb_value_changed(adj, tbl, "expansion");
3592 static void
3593 sp_spl_tb_t0_value_changed(GtkAdjustment *adj, GObject *tbl)
3595     sp_spl_tb_value_changed(adj, tbl, "t0");
3598 static void
3599 sp_spl_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3601     GtkWidget *tbl = GTK_WIDGET(obj);
3603     GtkAdjustment *adj;
3605     // fixme: make settable
3606     gdouble rev = 5;
3607     gdouble exp = 1.0;
3608     gdouble t0 = 0.0;
3610     adj = (GtkAdjustment*)gtk_object_get_data(obj, "revolution");
3611     gtk_adjustment_set_value(adj, rev);
3612     gtk_adjustment_value_changed(adj);
3614     adj = (GtkAdjustment*)gtk_object_get_data(obj, "expansion");
3615     gtk_adjustment_set_value(adj, exp);
3616     gtk_adjustment_value_changed(adj);
3618     adj = (GtkAdjustment*)gtk_object_get_data(obj, "t0");
3619     gtk_adjustment_set_value(adj, t0);
3620     gtk_adjustment_value_changed(adj);
3622     spinbutton_defocus(GTK_OBJECT(tbl));
3626 static void spiral_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
3627                                          gchar const */*old_value*/, gchar const */*new_value*/,
3628                                          bool /*is_interactive*/, gpointer data)
3630     GtkWidget *tbl = GTK_WIDGET(data);
3632     // quit if run by the _changed callbacks
3633     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
3634         return;
3635     }
3637     // in turn, prevent callbacks from responding
3638     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
3640     GtkAdjustment *adj;
3641     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "revolution");
3642     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:revolution", 3.0)));
3644     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "expansion");
3645     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:expansion", 1.0)));
3647     adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "t0");
3648     gtk_adjustment_set_value(adj, (sp_repr_get_double_attribute(repr, "sodipodi:t0", 0.0)));
3650     g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
3654 static Inkscape::XML::NodeEventVector spiral_tb_repr_events = {
3655     NULL, /* child_added */
3656     NULL, /* child_removed */
3657     spiral_tb_event_attr_changed,
3658     NULL, /* content_changed */
3659     NULL  /* order_changed */
3660 };
3662 static void
3663 sp_spiral_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
3665     int n_selected = 0;
3666     Inkscape::XML::Node *repr = NULL;
3668     purge_repr_listener( tbl, tbl );
3670     for (GSList const *items = selection->itemList();
3671          items != NULL;
3672          items = items->next)
3673     {
3674         if (SP_IS_SPIRAL((SPItem *) items->data)) {
3675             n_selected++;
3676             repr = SP_OBJECT_REPR((SPItem *) items->data);
3677         }
3678     }
3680     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
3682     if (n_selected == 0) {
3683         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
3684     } else if (n_selected == 1) {
3685         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3687         if (repr) {
3688             g_object_set_data( tbl, "repr", repr );
3689             Inkscape::GC::anchor(repr);
3690             sp_repr_add_listener(repr, &spiral_tb_repr_events, tbl);
3691             sp_repr_synthesize_events(repr, &spiral_tb_repr_events, tbl);
3692         }
3693     } else {
3694         // FIXME: implement averaging of all parameters for multiple selected
3695         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
3696         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
3697     }
3701 static void sp_spiral_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3703     EgeAdjustmentAction* eact = 0;
3704     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3706     {
3707         EgeOutputAction* act = ege_output_action_new( "SpiralStateAction", _("<b>New:</b>"), "", 0 );
3708         ege_output_action_set_use_markup( act, TRUE );
3709         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
3710         g_object_set_data( holder, "mode_action", act );
3711     }
3713     /* Revolution */
3714     {
3715         gchar const* labels[] = {_("just a curve"), 0, _("one full revolution"), 0, 0, 0, 0, 0, 0};
3716         gdouble values[] = {0.01, 0.5, 1, 2, 3, 5, 10, 20, 50, 100};
3717         eact = create_adjustment_action( "SpiralRevolutionAction",
3718                                          _("Number of turns"), _("Turns:"), _("Number of revolutions"),
3719                                          "/tools/shapes/spiral/revolution", 3.0,
3720                                          GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-spiral",
3721                                          0.01, 1024.0, 0.1, 1.0,
3722                                          labels, values, G_N_ELEMENTS(labels),
3723                                          sp_spl_tb_revolution_value_changed, 1, 2);
3724         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3725     }
3727     /* Expansion */
3728     {
3729         gchar const* labels[] = {_("circle"), _("edge is much denser"), _("edge is denser"), _("even"), _("center is denser"), _("center is much denser"), 0};
3730         gdouble values[] = {0, 0.1, 0.5, 1, 1.5, 5, 20};
3731         eact = create_adjustment_action( "SpiralExpansionAction",
3732                                          _("Divergence"), _("Divergence:"), _("How much denser/sparser are outer revolutions; 1 = uniform"),
3733                                          "/tools/shapes/spiral/expansion", 1.0,
3734                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3735                                          0.0, 1000.0, 0.01, 1.0,
3736                                          labels, values, G_N_ELEMENTS(labels),
3737                                          sp_spl_tb_expansion_value_changed);
3738         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3739     }
3741     /* T0 */
3742     {
3743         gchar const* labels[] = {_("starts from center"), _("starts mid-way"), _("starts near edge")};
3744         gdouble values[] = {0, 0.5, 0.9};
3745         eact = create_adjustment_action( "SpiralT0Action",
3746                                          _("Inner radius"), _("Inner radius:"), _("Radius of the innermost revolution (relative to the spiral size)"),
3747                                          "/tools/shapes/spiral/t0", 0.0,
3748                                          GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
3749                                          0.0, 0.999, 0.01, 1.0,
3750                                          labels, values, G_N_ELEMENTS(labels),
3751                                          sp_spl_tb_t0_value_changed);
3752         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
3753     }
3755     /* Reset */
3756     {
3757         InkAction* inky = ink_action_new( "SpiralResetAction",
3758                                           _("Defaults"),
3759                                           _("Reset shape parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
3760                                           GTK_STOCK_CLEAR,
3761                                           secondarySize );
3762         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_spl_tb_defaults), holder );
3763         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
3764     }
3767     sigc::connection *connection = new sigc::connection(
3768         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_spiral_toolbox_selection_changed), (GObject *)holder))
3769         );
3770     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
3771     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
3774 //########################
3775 //##     Pen/Pencil     ##
3776 //########################
3778 /* This is used in generic functions below to share large portions of code between pen and pencil tool */
3779 static Glib::ustring const
3780 freehand_tool_name(GObject *dataKludge)
3782     SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
3783     return ( tools_isactive(desktop, TOOLS_FREEHAND_PEN)
3784              ? "/tools/freehand/pen"
3785              : "/tools/freehand/pencil" );
3788 static void freehand_mode_changed(EgeSelectOneAction* act, GObject* tbl)
3790     gint mode = ege_select_one_action_get_active(act);
3792     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3793     prefs->setInt(freehand_tool_name(tbl) + "/freehand-mode", mode);
3795     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
3797     // in pen tool we have more options than in pencil tool; if one of them was chosen, we do any
3798     // preparatory work here
3799     if (SP_IS_PEN_CONTEXT(desktop->event_context)) {
3800         SPPenContext *pc = SP_PEN_CONTEXT(desktop->event_context);
3801         sp_pen_context_set_polyline_mode(pc);
3802     }
3805 static void sp_add_freehand_mode_toggle(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil)
3807     /* Freehand mode toggle buttons */
3808     {
3809         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3810         guint freehandMode = prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/freehand-mode" : "/tools/freehand/pen/freehand-mode" ), 0);
3811         Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
3813         {
3814             GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
3816             GtkTreeIter iter;
3817             gtk_list_store_append( model, &iter );
3818             gtk_list_store_set( model, &iter,
3819                                 0, _("Bezier"),
3820                                 1, _("Create regular Bezier path"),
3821                                 2, INKSCAPE_ICON_PATH_MODE_BEZIER,
3822                                 -1 );
3824             gtk_list_store_append( model, &iter );
3825             gtk_list_store_set( model, &iter,
3826                                 0, _("Spiro"),
3827                                 1, _("Create Spiro path"),
3828                                 2, INKSCAPE_ICON_PATH_MODE_SPIRO,
3829                                 -1 );
3831             if (!tool_is_pencil) {
3832                 gtk_list_store_append( model, &iter );
3833                 gtk_list_store_set( model, &iter,
3834                                     0, _("Zigzag"),
3835                                     1, _("Create a sequence of straight line segments"),
3836                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE,
3837                                     -1 );
3839                 gtk_list_store_append( model, &iter );
3840                 gtk_list_store_set( model, &iter,
3841                                     0, _("Paraxial"),
3842                                     1, _("Create a sequence of paraxial line segments"),
3843                                     2, INKSCAPE_ICON_PATH_MODE_POLYLINE_PARAXIAL,
3844                                     -1 );
3845             }
3847             EgeSelectOneAction* act = ege_select_one_action_new(tool_is_pencil ?
3848                                                                 "FreehandModeActionPencil" :
3849                                                                 "FreehandModeActionPen",
3850                                                                 (_("Mode:")), ("Mode"), NULL, GTK_TREE_MODEL(model) );
3851             gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
3853             ege_select_one_action_set_appearance( act, "full" );
3854             ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
3855             g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
3856             ege_select_one_action_set_icon_column( act, 2 );
3857             ege_select_one_action_set_icon_size( act, secondarySize );
3858             ege_select_one_action_set_tooltip_column( act, 1  );
3860             ege_select_one_action_set_active( act, freehandMode);
3861             g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(freehand_mode_changed), holder);
3862         }
3863     }
3866 static void freehand_change_shape(EgeSelectOneAction* act, GObject *dataKludge) {
3867     gint shape = ege_select_one_action_get_active( act );
3868     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3869     prefs->setInt(freehand_tool_name(dataKludge) + "/shape", shape);
3872 /**
3873  * \brief Generate the list of freehand advanced shape option entries.
3874  */
3875 GList * freehand_shape_dropdown_items_list() {
3876     GList *glist = NULL;
3878     glist = g_list_append (glist, _("None"));
3879     glist = g_list_append (glist, _("Triangle in"));
3880     glist = g_list_append (glist, _("Triangle out"));
3881     glist = g_list_append (glist, _("Ellipse"));
3882     glist = g_list_append (glist, _("From clipboard"));
3884     return glist;
3887 static void
3888 freehand_add_advanced_shape_options(GtkActionGroup* mainActions, GObject* holder, bool tool_is_pencil) {
3889     /*advanced shape options */
3890     {
3891         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3892         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
3894         GList* items = 0;
3895         gint count = 0;
3896         for ( items = freehand_shape_dropdown_items_list(); items ; items = g_list_next(items) )
3897         {
3898             GtkTreeIter iter;
3899             gtk_list_store_append( model, &iter );
3900             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
3901             count++;
3902         }
3903         g_list_free( items );
3904         items = 0;
3905         EgeSelectOneAction* act1 = ege_select_one_action_new(
3906             tool_is_pencil ? "SetPencilShapeAction" : "SetPenShapeAction",
3907             _("Shape:"), ("Shape"), NULL, GTK_TREE_MODEL(model));
3908         g_object_set( act1, "short_label", _("Shape:"), NULL );
3909         ege_select_one_action_set_appearance( act1, "compact" );
3910         ege_select_one_action_set_active( act1, prefs->getInt(( tool_is_pencil ? "/tools/freehand/pencil/shape" : "/tools/freehand/pen/shape" ), 0) );
3911         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(freehand_change_shape), holder );
3912         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
3913         g_object_set_data( holder, "shape_action", act1 );
3914     }
3917 static void sp_pen_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
3919     sp_add_freehand_mode_toggle(mainActions, holder, false);
3920     freehand_add_advanced_shape_options(mainActions, holder, false);
3924 static void
3925 sp_pencil_tb_defaults(GtkWidget */*widget*/, GtkObject *obj)
3927     GtkWidget *tbl = GTK_WIDGET(obj);
3929     GtkAdjustment *adj;
3931     // fixme: make settable
3932     gdouble tolerance = 4;
3934     adj = (GtkAdjustment*)gtk_object_get_data(obj, "tolerance");
3935     gtk_adjustment_set_value(adj, tolerance);
3936     gtk_adjustment_value_changed(adj);
3938     spinbutton_defocus(GTK_OBJECT(tbl));
3941 static void
3942 sp_pencil_tb_tolerance_value_changed(GtkAdjustment *adj, GObject *tbl)
3944     // quit if run by the attr_changed listener
3945     if (g_object_get_data( tbl, "freeze" )) {
3946         return;
3947     }
3948     // in turn, prevent listener from responding
3949     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3950     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3951     prefs->setDouble("/tools/freehand/pencil/tolerance", adj->value);
3952     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3955 class PencilToleranceObserver : public Inkscape::Preferences::Observer {
3956 public:
3957     PencilToleranceObserver(Glib::ustring const &path, GObject *x) : Observer(path), _obj(x)
3958     {
3959         g_object_set_data(_obj, "prefobserver", this);
3960     }
3961     virtual ~PencilToleranceObserver() {
3962         if (g_object_get_data(_obj, "prefobserver") == this) {
3963                 g_object_set_data(_obj, "prefobserver", NULL);
3964         }
3965     }
3966     virtual void notify(Inkscape::Preferences::Entry const &val) {
3967         GObject* tbl = _obj;
3968         if (g_object_get_data( tbl, "freeze" )) {
3969             return;
3970         }
3971         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
3973         GtkAdjustment * adj = (GtkAdjustment*)g_object_get_data(tbl, "tolerance");
3975         double v = val.getDouble(adj->value);
3976         gtk_adjustment_set_value(adj, v);
3977         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
3978     }
3979 private:
3980     GObject *_obj;
3981 };
3984 static void sp_pencil_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
3986     sp_add_freehand_mode_toggle(mainActions, holder, true);
3988     EgeAdjustmentAction* eact = 0;
3990     /* Tolerance */
3991     {
3992         gchar const* labels[] = {_("(many nodes, rough)"), _("(default)"), 0, 0, 0, 0, _("(few nodes, smooth)")};
3993         gdouble values[] = {1, 10, 20, 30, 50, 75, 100};
3994         eact = create_adjustment_action( "PencilToleranceAction",
3995                                          _("Smoothing:"), _("Smoothing: "),
3996                  _("How much smoothing (simplifying) is applied to the line"),
3997                                          "/tools/freehand/pencil/tolerance",
3998                                          3.0,
3999                                          GTK_WIDGET(desktop->canvas), NULL,
4000                                          holder, TRUE, "altx-pencil",
4001                                          1, 100.0, 0.5, 0,
4002                                          labels, values, G_N_ELEMENTS(labels),
4003                                          sp_pencil_tb_tolerance_value_changed,
4004                                          1, 2);
4005         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4006         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4008         PencilToleranceObserver *obs =
4009             new PencilToleranceObserver("/tools/freehand/pencil/tolerance", G_OBJECT(holder));
4010     }
4012     /* advanced shape options */
4013     freehand_add_advanced_shape_options(mainActions, holder, true);
4015     /* Reset */
4016     {
4017         InkAction* inky = ink_action_new( "PencilResetAction",
4018                                           _("Defaults"),
4019                                           _("Reset pencil parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
4020                                           GTK_STOCK_CLEAR,
4021                                           Inkscape::ICON_SIZE_SMALL_TOOLBAR );
4022         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_pencil_tb_defaults), holder );
4023         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
4024     }
4026     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
4031 //########################
4032 //##       Tweak        ##
4033 //########################
4035 static void sp_tweak_width_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4037     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4038     prefs->setDouble( "/tools/tweak/width", adj->value * 0.01 );
4041 static void sp_tweak_force_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4043     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4044     prefs->setDouble( "/tools/tweak/force", adj->value * 0.01 );
4047 static void sp_tweak_pressure_state_changed( GtkToggleAction *act, gpointer /*data*/ )
4049     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4050     prefs->setBool("/tools/tweak/usepressure", gtk_toggle_action_get_active(act));
4053 static void sp_tweak_mode_changed( EgeSelectOneAction *act, GObject *tbl )
4055     int mode = ege_select_one_action_get_active( act );
4056     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4057     prefs->setInt("/tools/tweak/mode", mode);
4059     GtkAction *doh = GTK_ACTION(g_object_get_data( tbl, "tweak_doh"));
4060     GtkAction *dos = GTK_ACTION(g_object_get_data( tbl, "tweak_dos"));
4061     GtkAction *dol = GTK_ACTION(g_object_get_data( tbl, "tweak_dol"));
4062     GtkAction *doo = GTK_ACTION(g_object_get_data( tbl, "tweak_doo"));
4063     GtkAction *fid = GTK_ACTION(g_object_get_data( tbl, "tweak_fidelity"));
4064     GtkAction *dolabel = GTK_ACTION(g_object_get_data( tbl, "tweak_channels_label"));
4065     if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER) {
4066         if (doh) gtk_action_set_sensitive (doh, TRUE);
4067         if (dos) gtk_action_set_sensitive (dos, TRUE);
4068         if (dol) gtk_action_set_sensitive (dol, TRUE);
4069         if (doo) gtk_action_set_sensitive (doo, TRUE);
4070         if (dolabel) gtk_action_set_sensitive (dolabel, TRUE);
4071         if (fid) gtk_action_set_sensitive (fid, FALSE);
4072     } else {
4073         if (doh) gtk_action_set_sensitive (doh, FALSE);
4074         if (dos) gtk_action_set_sensitive (dos, FALSE);
4075         if (dol) gtk_action_set_sensitive (dol, FALSE);
4076         if (doo) gtk_action_set_sensitive (doo, FALSE);
4077         if (dolabel) gtk_action_set_sensitive (dolabel, FALSE);
4078         if (fid) gtk_action_set_sensitive (fid, TRUE);
4079     }
4082 static void sp_tweak_fidelity_value_changed( GtkAdjustment *adj, GObject */*tbl*/ )
4084     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4085     prefs->setDouble( "/tools/tweak/fidelity", adj->value * 0.01 );
4088 static void tweak_toggle_doh (GtkToggleAction *act, gpointer /*data*/) {
4089     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4090     prefs->setBool("/tools/tweak/doh", gtk_toggle_action_get_active(act));
4092 static void tweak_toggle_dos (GtkToggleAction *act, gpointer /*data*/) {
4093     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4094     prefs->setBool("/tools/tweak/dos", gtk_toggle_action_get_active(act));
4096 static void tweak_toggle_dol (GtkToggleAction *act, gpointer /*data*/) {
4097     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4098     prefs->setBool("/tools/tweak/dol", gtk_toggle_action_get_active(act));
4100 static void tweak_toggle_doo (GtkToggleAction *act, gpointer /*data*/) {
4101     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4102     prefs->setBool("/tools/tweak/doo", gtk_toggle_action_get_active(act));
4105 static void sp_tweak_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4107     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
4108     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4110     {
4111         /* Width */
4112         gchar const* labels[] = {_("(pinch tweak)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad tweak)")};
4113         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4114         EgeAdjustmentAction *eact = create_adjustment_action( "TweakWidthAction",
4115                                                               _("Width"), _("Width:"), _("The width of the tweak area (relative to the visible canvas area)"),
4116                                                               "/tools/tweak/width", 15,
4117                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-tweak",
4118                                                               1, 100, 1.0, 0.0,
4119                                                               labels, values, G_N_ELEMENTS(labels),
4120                                                               sp_tweak_width_value_changed,  0.01, 0, 100 );
4121         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4122         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4123         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4124     }
4127     {
4128         /* Force */
4129         gchar const* labels[] = {_("(minimum force)"), 0, 0, _("(default)"), 0, 0, 0, _("(maximum force)")};
4130         gdouble values[] = {1, 5, 10, 20, 30, 50, 70, 100};
4131         EgeAdjustmentAction *eact = create_adjustment_action( "TweakForceAction",
4132                                                               _("Force"), _("Force:"), _("The force of the tweak action"),
4133                                                               "/tools/tweak/force", 20,
4134                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-force",
4135                                                               1, 100, 1.0, 0.0,
4136                                                               labels, values, G_N_ELEMENTS(labels),
4137                                                               sp_tweak_force_value_changed,  0.01, 0, 100 );
4138         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4139         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4140         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4141     }
4143     /* Mode */
4144     {
4145         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
4147         GtkTreeIter iter;
4148         gtk_list_store_append( model, &iter );
4149         gtk_list_store_set( model, &iter,
4150                             0, _("Move mode"),
4151                             1, _("Move objects in any direction"),
4152                             2, INKSCAPE_ICON_OBJECT_TWEAK_PUSH,
4153                             -1 );
4155         gtk_list_store_append( model, &iter );
4156         gtk_list_store_set( model, &iter,
4157                             0, _("Move in/out mode"),
4158                             1, _("Move objects towards cursor; with Shift from cursor"),
4159                             2, INKSCAPE_ICON_OBJECT_TWEAK_ATTRACT,
4160                             -1 );
4162         gtk_list_store_append( model, &iter );
4163         gtk_list_store_set( model, &iter,
4164                             0, _("Move jitter mode"),
4165                             1, _("Move objects in random directions"),
4166                             2, INKSCAPE_ICON_OBJECT_TWEAK_RANDOMIZE,
4167                             -1 );
4169         gtk_list_store_append( model, &iter );
4170         gtk_list_store_set( model, &iter,
4171                             0, _("Scale mode"),
4172                             1, _("Shrink objects, with Shift enlarge"),
4173                             2, INKSCAPE_ICON_OBJECT_TWEAK_SHRINK,
4174                             -1 );
4176         gtk_list_store_append( model, &iter );
4177         gtk_list_store_set( model, &iter,
4178                             0, _("Rotate mode"),
4179                             1, _("Rotate objects, with Shift counterclockwise"),
4180                             2, INKSCAPE_ICON_OBJECT_TWEAK_ROTATE,
4181                             -1 );
4183         gtk_list_store_append( model, &iter );
4184         gtk_list_store_set( model, &iter,
4185                             0, _("Duplicate/delete mode"),
4186                             1, _("Duplicate objects, with Shift delete"),
4187                             2, INKSCAPE_ICON_OBJECT_TWEAK_DUPLICATE,
4188                             -1 );
4190         gtk_list_store_append( model, &iter );
4191         gtk_list_store_set( model, &iter,
4192                             0, _("Push mode"),
4193                             1, _("Push parts of paths in any direction"),
4194                             2, INKSCAPE_ICON_PATH_TWEAK_PUSH,
4195                             -1 );
4197         gtk_list_store_append( model, &iter );
4198         gtk_list_store_set( model, &iter,
4199                             0, _("Shrink/grow mode"),
4200                             1, _("Shrink (inset) parts of paths; with Shift grow (outset)"),
4201                             2, INKSCAPE_ICON_PATH_TWEAK_SHRINK,
4202                             -1 );
4204         gtk_list_store_append( model, &iter );
4205         gtk_list_store_set( model, &iter,
4206                             0, _("Attract/repel mode"),
4207                             1, _("Attract parts of paths towards cursor; with Shift from cursor"),
4208                             2, INKSCAPE_ICON_PATH_TWEAK_ATTRACT,
4209                             -1 );
4211         gtk_list_store_append( model, &iter );
4212         gtk_list_store_set( model, &iter,
4213                             0, _("Roughen mode"),
4214                             1, _("Roughen parts of paths"),
4215                             2, INKSCAPE_ICON_PATH_TWEAK_ROUGHEN,
4216                             -1 );
4218         gtk_list_store_append( model, &iter );
4219         gtk_list_store_set( model, &iter,
4220                             0, _("Color paint mode"),
4221                             1, _("Paint the tool's color upon selected objects"),
4222                             2, INKSCAPE_ICON_OBJECT_TWEAK_PAINT,
4223                             -1 );
4225         gtk_list_store_append( model, &iter );
4226         gtk_list_store_set( model, &iter,
4227                             0, _("Color jitter mode"),
4228                             1, _("Jitter the colors of selected objects"),
4229                             2, INKSCAPE_ICON_OBJECT_TWEAK_JITTER_COLOR,
4230                             -1 );
4232         gtk_list_store_append( model, &iter );
4233         gtk_list_store_set( model, &iter,
4234                             0, _("Blur mode"),
4235                             1, _("Blur selected objects more; with Shift, blur less"),
4236                             2, INKSCAPE_ICON_OBJECT_TWEAK_BLUR,
4237                             -1 );
4240         EgeSelectOneAction* act = ege_select_one_action_new( "TweakModeAction", _("Mode"), (""), NULL, GTK_TREE_MODEL(model) );
4241         g_object_set( act, "short_label", _("Mode:"), NULL );
4242         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
4243         g_object_set_data( holder, "mode_action", act );
4245         ege_select_one_action_set_appearance( act, "full" );
4246         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
4247         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
4248         ege_select_one_action_set_icon_column( act, 2 );
4249         ege_select_one_action_set_icon_size( act, secondarySize );
4250         ege_select_one_action_set_tooltip_column( act, 1  );
4252         gint mode = prefs->getInt("/tools/tweak/mode", 0);
4253         ege_select_one_action_set_active( act, mode );
4254         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_tweak_mode_changed), holder );
4256         g_object_set_data( G_OBJECT(holder), "tweak_tool_mode", act);
4257     }
4259     guint mode = prefs->getInt("/tools/tweak/mode", 0);
4261     {
4262         EgeOutputAction* act = ege_output_action_new( "TweakChannelsLabel", _("Channels:"), "", 0 );
4263         ege_output_action_set_use_markup( act, TRUE );
4264         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4265         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4266             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4267         g_object_set_data( holder, "tweak_channels_label", act);
4268     }
4270     {
4271         InkToggleAction* act = ink_toggle_action_new( "TweakDoH",
4272                                                       _("Hue"),
4273                                                       _("In color mode, act on objects' hue"),
4274                                                       NULL,
4275                                                       Inkscape::ICON_SIZE_DECORATION );
4276         //TRANSLATORS:  "H" here stands for hue
4277         g_object_set( act, "short_label", _("H"), NULL );
4278         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4279         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doh), desktop );
4280         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doh", true) );
4281         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4282             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4283         g_object_set_data( holder, "tweak_doh", act);
4284     }
4285     {
4286         InkToggleAction* act = ink_toggle_action_new( "TweakDoS",
4287                                                       _("Saturation"),
4288                                                       _("In color mode, act on objects' saturation"),
4289                                                       NULL,
4290                                                       Inkscape::ICON_SIZE_DECORATION );
4291         //TRANSLATORS: "S" here stands for Saturation
4292         g_object_set( act, "short_label", _("S"), NULL );
4293         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4294         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dos), desktop );
4295         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dos", true) );
4296         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4297             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4298         g_object_set_data( holder, "tweak_dos", act );
4299     }
4300     {
4301         InkToggleAction* act = ink_toggle_action_new( "TweakDoL",
4302                                                       _("Lightness"),
4303                                                       _("In color mode, act on objects' lightness"),
4304                                                       NULL,
4305                                                       Inkscape::ICON_SIZE_DECORATION );
4306         //TRANSLATORS: "L" here stands for Lightness
4307         g_object_set( act, "short_label", _("L"), NULL );
4308         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4309         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_dol), desktop );
4310         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/dol", true) );
4311         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4312             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4313         g_object_set_data( holder, "tweak_dol", act );
4314     }
4315     {
4316         InkToggleAction* act = ink_toggle_action_new( "TweakDoO",
4317                                                       _("Opacity"),
4318                                                       _("In color mode, act on objects' opacity"),
4319                                                       NULL,
4320                                                       Inkscape::ICON_SIZE_DECORATION );
4321         //TRANSLATORS: "O" here stands for Opacity
4322         g_object_set( act, "short_label", _("O"), NULL );
4323         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4324         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(tweak_toggle_doo), desktop );
4325         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/doo", true) );
4326         if (mode != TWEAK_MODE_COLORPAINT && mode != TWEAK_MODE_COLORJITTER)
4327             gtk_action_set_sensitive (GTK_ACTION(act), FALSE);
4328         g_object_set_data( holder, "tweak_doo", act );
4329     }
4331     {   /* Fidelity */
4332         gchar const* labels[] = {_("(rough, simplified)"), 0, 0, _("(default)"), 0, 0, _("(fine, but many nodes)")};
4333         gdouble values[] = {10, 25, 35, 50, 60, 80, 100};
4334         EgeAdjustmentAction *eact = create_adjustment_action( "TweakFidelityAction",
4335                                                               _("Fidelity"), _("Fidelity:"),
4336                                                               _("Low fidelity simplifies paths; high fidelity preserves path features but may generate a lot of new nodes"),
4337                                                               "/tools/tweak/fidelity", 50,
4338                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "tweak-fidelity",
4339                                                               1, 100, 1.0, 10.0,
4340                                                               labels, values, G_N_ELEMENTS(labels),
4341                                                               sp_tweak_fidelity_value_changed,  0.01, 0, 100 );
4342         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4343         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4344         if (mode == TWEAK_MODE_COLORPAINT || mode == TWEAK_MODE_COLORJITTER)
4345             gtk_action_set_sensitive (GTK_ACTION(eact), FALSE);
4346         g_object_set_data( holder, "tweak_fidelity", eact );
4347     }
4350     /* Use Pressure button */
4351     {
4352         InkToggleAction* act = ink_toggle_action_new( "TweakPressureAction",
4353                                                       _("Pressure"),
4354                                                       _("Use the pressure of the input device to alter the force of tweak action"),
4355                                                       "use_pressure",
4356                                                       Inkscape::ICON_SIZE_DECORATION );
4357         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4358         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_tweak_pressure_state_changed), NULL);
4359         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/tweak/usepressure", true) );
4360     }
4365 //########################
4366 //##     Calligraphy    ##
4367 //########################
4368 static void update_presets_list (GObject *tbl)
4370     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4371     if (g_object_get_data(tbl, "presets_blocked"))
4372         return;
4374     EgeSelectOneAction *sel = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4375     if (!sel) {
4376         // WTF!? This will cause a segfault if ever reached
4377         //ege_select_one_action_set_active(sel, 0);
4378         return;
4379     }
4381     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4383     int ege_index = 1;
4384     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++ege_index) {
4385         bool match = true;
4387         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(*i);
4388         for (std::vector<Inkscape::Preferences::Entry>::iterator j = preset.begin(); j != preset.end(); ++j) {
4389             Glib::ustring entry_name = j->getEntryName();
4390             if (entry_name == "id" || entry_name == "name") continue;
4392             void *widget = g_object_get_data(tbl, entry_name.data());
4393             if (widget) {
4394                 if (GTK_IS_ADJUSTMENT(widget)) {
4395                     double v = j->getDouble();
4396                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4397                     //std::cout << "compared adj " << attr_name << gtk_adjustment_get_value(adj) << " to " << v << "\n";
4398                     if (fabs(gtk_adjustment_get_value(adj) - v) > 1e-6) {
4399                         match = false;
4400                         break;
4401                     }
4402                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4403                     bool v = j->getBool();
4404                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4405                     //std::cout << "compared toggle " << attr_name << gtk_toggle_action_get_active(toggle) << " to " << v << "\n";
4406                     if ( static_cast<bool>(gtk_toggle_action_get_active(toggle)) != v ) {
4407                         match = false;
4408                         break;
4409                     }
4410                 }
4411             }
4412         }
4414         if (match) {
4415             // newly added item is at the same index as the
4416             // save command, so we need to change twice for it to take effect
4417             ege_select_one_action_set_active(sel, 0);
4418             ege_select_one_action_set_active(sel, ege_index); // one-based index
4419             return;
4420         }
4421     }
4423     // no match found
4424     ege_select_one_action_set_active(sel, 0);
4427 static void sp_ddc_mass_value_changed( GtkAdjustment *adj, GObject* tbl )
4429     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4430     prefs->setDouble( "/tools/calligraphic/mass", adj->value * 0.01 );
4431     update_presets_list(tbl);
4434 static void sp_ddc_wiggle_value_changed( GtkAdjustment *adj, GObject* tbl )
4436     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4437     prefs->setDouble( "/tools/calligraphic/wiggle", adj->value * 0.01 );
4438     update_presets_list(tbl);
4441 static void sp_ddc_angle_value_changed( GtkAdjustment *adj, GObject* tbl )
4443     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4444     prefs->setDouble( "/tools/calligraphic/angle", adj->value );
4445     update_presets_list(tbl);
4448 static void sp_ddc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
4450     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4451     prefs->setDouble( "/tools/calligraphic/width", adj->value * 0.01 );
4452     update_presets_list(tbl);
4455 static void sp_ddc_velthin_value_changed( GtkAdjustment *adj, GObject* tbl )
4457     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4458     prefs->setDouble("/tools/calligraphic/thinning", adj->value * 0.01 );
4459     update_presets_list(tbl);
4462 static void sp_ddc_flatness_value_changed( GtkAdjustment *adj, GObject* tbl )
4464     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4465     prefs->setDouble( "/tools/calligraphic/flatness", adj->value * 0.01);
4466     update_presets_list(tbl);
4469 static void sp_ddc_tremor_value_changed( GtkAdjustment *adj, GObject* tbl )
4471     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4472     prefs->setDouble( "/tools/calligraphic/tremor", adj->value * 0.01 );
4473     update_presets_list(tbl);
4476 static void sp_ddc_cap_rounding_value_changed( GtkAdjustment *adj, GObject* tbl )
4478     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4479     prefs->setDouble( "/tools/calligraphic/cap_rounding", adj->value );
4480     update_presets_list(tbl);
4483 static void sp_ddc_pressure_state_changed( GtkToggleAction *act, GObject*  tbl )
4485     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4486     prefs->setBool("/tools/calligraphic/usepressure", gtk_toggle_action_get_active( act ));
4487     update_presets_list(tbl);
4490 static void sp_ddc_trace_background_changed( GtkToggleAction *act, GObject*  tbl )
4492     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4493     prefs->setBool("/tools/calligraphic/tracebackground", gtk_toggle_action_get_active( act ));
4494     update_presets_list(tbl);
4497 static void sp_ddc_tilt_state_changed( GtkToggleAction *act, GObject*  tbl )
4499     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4500     GtkAction * calligraphy_angle = static_cast<GtkAction *> (g_object_get_data(tbl,"angle_action"));
4501     prefs->setBool("/tools/calligraphic/usetilt", gtk_toggle_action_get_active( act ));
4502     update_presets_list(tbl);
4503     if (calligraphy_angle )
4504         gtk_action_set_sensitive( calligraphy_angle, !gtk_toggle_action_get_active( act ) );
4508 static gchar const *const widget_names[] = {
4509     "width",
4510     "mass",
4511     "wiggle",
4512     "angle",
4513     "thinning",
4514     "tremor",
4515     "flatness",
4516     "cap_rounding",
4517     "usepressure",
4518     "tracebackground",
4519     "usetilt"
4520 };
4523 static void sp_dcc_build_presets_list(GObject *tbl)
4525     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4527     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "profile_selector"));
4528     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
4529     gtk_list_store_clear (model);
4531     {
4532         GtkTreeIter iter;
4533         gtk_list_store_append( model, &iter );
4534         gtk_list_store_set( model, &iter, 0, _("No preset"), 1, 0, -1 );
4535     }
4537     // iterate over all presets to populate the list
4538     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4539     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4540     int ii=1;
4542     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i) {
4543         GtkTreeIter iter;
4544         Glib::ustring preset_name = prefs->getString(*i + "/name");
4545         gtk_list_store_append( model, &iter );
4546         gtk_list_store_set( model, &iter, 0, preset_name.data(), 1, ii++, -1 );
4547     }
4549     {
4550         GtkTreeIter iter;
4551         gtk_list_store_append( model, &iter );
4552         gtk_list_store_set( model, &iter, 0, _("Save..."), 1, ii, -1 );
4553         g_object_set_data(tbl, "save_presets_index", GINT_TO_POINTER(ii));
4554     }
4556     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4558     update_presets_list (tbl);
4561 static void sp_dcc_save_profile (GtkWidget */*widget*/, GObject *tbl)
4563     using Inkscape::UI::Dialog::CalligraphicProfileRename;
4564     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4565     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop" );
4566     if (! desktop) return;
4568     if (g_object_get_data(tbl, "presets_blocked"))
4569         return;
4571     CalligraphicProfileRename::show(desktop);
4572     if ( !CalligraphicProfileRename::applied()) {
4573         // dialog cancelled
4574         update_presets_list (tbl);
4575         return;
4576     }
4577     Glib::ustring profile_name = CalligraphicProfileRename::getProfileName();
4579     if (profile_name.empty()) {
4580         // empty name entered
4581         update_presets_list (tbl);
4582         return;
4583     }
4585     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(TRUE));
4587     // If there's a preset with the given name, find it and set save_path appropriately
4588     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4589     int total_presets = presets.size();
4590     int new_index = -1;
4591     Glib::ustring save_path; // profile pref path without a trailing slash
4593     int temp_index = 0;
4594     for (std::vector<Glib::ustring>::iterator i = presets.begin(); i != presets.end(); ++i, ++temp_index) {
4595         Glib::ustring name = prefs->getString(*i + "/name");
4596         if (!name.empty() && profile_name == name) {
4597             new_index = temp_index;
4598             save_path = *i;
4599             break;
4600         }
4601     }
4603     if (new_index == -1) {
4604         // no preset with this name, create
4605         new_index = total_presets + 1;
4606         gchar *profile_id = g_strdup_printf("/dcc%d", new_index);
4607         save_path = Glib::ustring("/tools/calligraphic/preset") + profile_id;
4608         g_free(profile_id);
4609     }
4611     for (unsigned i = 0; i < G_N_ELEMENTS(widget_names); ++i) {
4612         gchar const *const widget_name = widget_names[i];
4613         void *widget = g_object_get_data(tbl, widget_name);
4614         if (widget) {
4615             if (GTK_IS_ADJUSTMENT(widget)) {
4616                 GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4617                 prefs->setDouble(save_path + "/" + widget_name, gtk_adjustment_get_value(adj));
4618                 //std::cout << "wrote adj " << widget_name << ": " << v << "\n";
4619             } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4620                 GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4621                 prefs->setBool(save_path + "/" + widget_name, gtk_toggle_action_get_active(toggle));
4622                 //std::cout << "wrote tog " << widget_name << ": " << v << "\n";
4623             } else {
4624                 g_warning("Unknown widget type for preset: %s\n", widget_name);
4625             }
4626         } else {
4627             g_warning("Bad key when writing preset: %s\n", widget_name);
4628         }
4629     }
4630     prefs->setString(save_path + "/name", profile_name);
4632     g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4633     sp_dcc_build_presets_list (tbl);
4637 static void sp_ddc_change_profile(EgeSelectOneAction* act, GObject* tbl) {
4639     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4641     gint preset_index = ege_select_one_action_get_active( act );
4642     // This is necessary because EgeSelectOneAction spams us with GObject "changed" signal calls
4643     // even when the preset is not changed. It would be good to replace it with something more
4644     // modern. Index 0 means "No preset", so we don't do anything.
4645     if (preset_index == 0) return;
4647     gint save_presets_index = GPOINTER_TO_INT(g_object_get_data(tbl, "save_presets_index"));
4649     if (preset_index == save_presets_index) {
4650         // this is the Save command
4651         sp_dcc_save_profile(NULL, tbl);
4652         return;
4653     }
4655     if (g_object_get_data(tbl, "presets_blocked"))
4656         return;
4658     // preset_index is one-based so we subtract 1
4659     std::vector<Glib::ustring> presets = prefs->getAllDirs("/tools/calligraphic/preset");
4660     Glib::ustring preset_path = presets.at(preset_index - 1);
4662     if (!preset_path.empty()) {
4663         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
4665         std::vector<Inkscape::Preferences::Entry> preset = prefs->getAllEntries(preset_path);
4667         // Shouldn't this be std::map?
4668         for (std::vector<Inkscape::Preferences::Entry>::iterator i = preset.begin(); i != preset.end(); ++i) {
4669             Glib::ustring entry_name = i->getEntryName();
4670             if (entry_name == "id" || entry_name == "name") continue;
4671             void *widget = g_object_get_data(tbl, entry_name.data());
4672             if (widget) {
4673                 if (GTK_IS_ADJUSTMENT(widget)) {
4674                     GtkAdjustment* adj = static_cast<GtkAdjustment *>(widget);
4675                     gtk_adjustment_set_value(adj, i->getDouble());
4676                     //std::cout << "set adj " << attr_name << " to " << v << "\n";
4677                 } else if (GTK_IS_TOGGLE_ACTION(widget)) {
4678                     GtkToggleAction* toggle = static_cast<GtkToggleAction *>(widget);
4679                     gtk_toggle_action_set_active(toggle, i->getBool());
4680                     //std::cout << "set toggle " << attr_name << " to " << v << "\n";
4681                 } else {
4682                     g_warning("Unknown widget type for preset: %s\n", entry_name.data());
4683                 }
4684             } else {
4685                 g_warning("Bad key found in a preset record: %s\n", entry_name.data());
4686             }
4687         }
4688         g_object_set_data(tbl, "presets_blocked", GINT_TO_POINTER(FALSE));
4689     }
4693 static void sp_calligraphy_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
4695     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4696     {
4697         g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(TRUE));
4699         EgeAdjustmentAction* calligraphy_angle = 0;
4701         {
4702         /* Width */
4703         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
4704         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
4705         EgeAdjustmentAction *eact = create_adjustment_action( "CalligraphyWidthAction",
4706                                                               _("Pen Width"), _("Width:"),
4707                                                               _("The width of the calligraphic pen (relative to the visible canvas area)"),
4708                                                               "/tools/calligraphic/width", 15,
4709                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-calligraphy",
4710                                                               1, 100, 1.0, 0.0,
4711                                                               labels, values, G_N_ELEMENTS(labels),
4712                                                               sp_ddc_width_value_changed,  0.01, 0, 100 );
4713         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4714         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4715         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4716         }
4718         {
4719         /* Thinning */
4720             gchar const* labels[] = {_("(speed blows up stroke)"), 0, 0, _("(slight widening)"), _("(constant width)"), _("(slight thinning, default)"), 0, 0, _("(speed deflates stroke)")};
4721             gdouble values[] = {-100, -40, -20, -10, 0, 10, 20, 40, 100};
4722         EgeAdjustmentAction* eact = create_adjustment_action( "ThinningAction",
4723                                                               _("Stroke Thinning"), _("Thinning:"),
4724                                                               _("How much velocity thins the stroke (> 0 makes fast strokes thinner, < 0 makes them broader, 0 makes width independent of velocity)"),
4725                                                               "/tools/calligraphic/thinning", 10,
4726                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4727                                                               -100, 100, 1, 0.1,
4728                                                               labels, values, G_N_ELEMENTS(labels),
4729                                                               sp_ddc_velthin_value_changed, 0.01, 0, 100);
4730         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4731         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4732         }
4734         {
4735         /* Angle */
4736         gchar const* labels[] = {_("(left edge up)"), 0, 0, _("(horizontal)"), _("(default)"), 0, _("(right edge up)")};
4737         gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
4738         EgeAdjustmentAction* eact = create_adjustment_action( "AngleAction",
4739                                                               _("Pen Angle"), _("Angle:"),
4740                                                               _("The angle of the pen's nib (in degrees; 0 = horizontal; has no effect if fixation = 0)"),
4741                                                               "/tools/calligraphic/angle", 30,
4742                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "calligraphy-angle",
4743                                                               -90.0, 90.0, 1.0, 10.0,
4744                                                               labels, values, G_N_ELEMENTS(labels),
4745                                                               sp_ddc_angle_value_changed, 1, 0 );
4746         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4747         g_object_set_data( holder, "angle_action", eact );
4748         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4749         calligraphy_angle = eact;
4750         }
4752         {
4753         /* Fixation */
4754             gchar const* labels[] = {_("(perpendicular to stroke, \"brush\")"), 0, 0, 0, _("(almost fixed, default)"), _("(fixed by Angle, \"pen\")")};
4755         gdouble values[] = {0, 20, 40, 60, 90, 100};
4756         EgeAdjustmentAction* eact = create_adjustment_action( "FixationAction",
4757                                                               _("Fixation"), _("Fixation:"),
4758                                                               _("Angle behavior (0 = nib always perpendicular to stroke direction, 100 = fixed angle)"),
4759                                                               "/tools/calligraphic/flatness", 90,
4760                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4761                                                               0.0, 100, 1.0, 10.0,
4762                                                               labels, values, G_N_ELEMENTS(labels),
4763                                                               sp_ddc_flatness_value_changed, 0.01, 0, 100 );
4764         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4765         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4766         }
4768         {
4769         /* Cap Rounding */
4770             gchar const* labels[] = {_("(blunt caps, default)"), _("(slightly bulging)"), 0, 0, _("(approximately round)"), _("(long protruding caps)")};
4771         gdouble values[] = {0, 0.3, 0.5, 1.0, 1.4, 5.0};
4772         // TRANSLATORS: "cap" means "end" (both start and finish) here
4773         EgeAdjustmentAction* eact = create_adjustment_action( "CapRoundingAction",
4774                                                               _("Cap rounding"), _("Caps:"),
4775                                                               _("Increase to make caps at the ends of strokes protrude more (0 = no caps, 1 = round caps)"),
4776                                                               "/tools/calligraphic/cap_rounding", 0.0,
4777                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4778                                                               0.0, 5.0, 0.01, 0.1,
4779                                                               labels, values, G_N_ELEMENTS(labels),
4780                                                               sp_ddc_cap_rounding_value_changed, 0.01, 2 );
4781         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4782         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4783         }
4785         {
4786         /* Tremor */
4787             gchar const* labels[] = {_("(smooth line)"), _("(slight tremor)"), _("(noticeable tremor)"), 0, 0, _("(maximum tremor)")};
4788         gdouble values[] = {0, 10, 20, 40, 60, 100};
4789         EgeAdjustmentAction* eact = create_adjustment_action( "TremorAction",
4790                                                               _("Stroke Tremor"), _("Tremor:"),
4791                                                               _("Increase to make strokes rugged and trembling"),
4792                                                               "/tools/calligraphic/tremor", 0.0,
4793                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4794                                                               0.0, 100, 1, 0.0,
4795                                                               labels, values, G_N_ELEMENTS(labels),
4796                                                               sp_ddc_tremor_value_changed, 0.01, 0, 100 );
4798         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4799         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4800         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4801         }
4803         {
4804         /* Wiggle */
4805         gchar const* labels[] = {_("(no wiggle)"), _("(slight deviation)"), 0, 0, _("(wild waves and curls)")};
4806         gdouble values[] = {0, 20, 40, 60, 100};
4807         EgeAdjustmentAction* eact = create_adjustment_action( "WiggleAction",
4808                                                               _("Pen Wiggle"), _("Wiggle:"),
4809                                                               _("Increase to make the pen waver and wiggle"),
4810                                                               "/tools/calligraphic/wiggle", 0.0,
4811                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4812                                                               0.0, 100, 1, 0.0,
4813                                                               labels, values, G_N_ELEMENTS(labels),
4814                                                               sp_ddc_wiggle_value_changed, 0.01, 0, 100 );
4815         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4816         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4817         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4818         }
4820         {
4821         /* Mass */
4822             gchar const* labels[] = {_("(no inertia)"), _("(slight smoothing, default)"), _("(noticeable lagging)"), 0, 0, _("(maximum inertia)")};
4823         gdouble values[] = {0.0, 2, 10, 20, 50, 100};
4824         EgeAdjustmentAction* eact = create_adjustment_action( "MassAction",
4825                                                               _("Pen Mass"), _("Mass:"),
4826                                                               _("Increase to make the pen drag behind, as if slowed by inertia"),
4827                                                               "/tools/calligraphic/mass", 2.0,
4828                                                               GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
4829                                                               0.0, 100, 1, 0.0,
4830                                                               labels, values, G_N_ELEMENTS(labels),
4831                                                               sp_ddc_mass_value_changed, 0.01, 0, 100 );
4832         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
4833         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
4834         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
4835         }
4838         /* Trace Background button */
4839         {
4840             InkToggleAction* act = ink_toggle_action_new( "TraceAction",
4841                                                           _("Trace Background"),
4842                                                           _("Trace the lightness of the background by the width of the pen (white - minimum width, black - maximum width)"),
4843                                                           INKSCAPE_ICON_DRAW_TRACE_BACKGROUND,
4844                                                           Inkscape::ICON_SIZE_DECORATION );
4845             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4846             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_trace_background_changed), holder);
4847             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/tracebackground", false) );
4848             g_object_set_data( holder, "tracebackground", act );
4849         }
4851         /* Use Pressure button */
4852         {
4853             InkToggleAction* act = ink_toggle_action_new( "PressureAction",
4854                                                           _("Pressure"),
4855                                                           _("Use the pressure of the input device to alter the width of the pen"),
4856                                                           INKSCAPE_ICON_DRAW_USE_PRESSURE,
4857                                                           Inkscape::ICON_SIZE_DECORATION );
4858             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4859             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_pressure_state_changed), holder);
4860             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usepressure", true) );
4861             g_object_set_data( holder, "usepressure", act );
4862         }
4864         /* Use Tilt button */
4865         {
4866             InkToggleAction* act = ink_toggle_action_new( "TiltAction",
4867                                                           _("Tilt"),
4868                                                           _("Use the tilt of the input device to alter the angle of the pen's nib"),
4869                                                           INKSCAPE_ICON_DRAW_USE_TILT,
4870                                                           Inkscape::ICON_SIZE_DECORATION );
4871             gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
4872             g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_ddc_tilt_state_changed), holder );
4873             gtk_action_set_sensitive( GTK_ACTION(calligraphy_angle), !prefs->getBool("/tools/calligraphic/usetilt", true) );
4874             gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool("/tools/calligraphic/usetilt", true) );
4875             g_object_set_data( holder, "usetilt", act );
4876         }
4878         /*calligraphic profile */
4879         {
4880             GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
4881             EgeSelectOneAction* act1 = ege_select_one_action_new ("SetProfileAction", "" , (_("Choose a preset")), NULL, GTK_TREE_MODEL(model));
4882             ege_select_one_action_set_appearance (act1, "compact");
4883             g_object_set_data (holder, "profile_selector", act1 );
4885             g_object_set_data(holder, "presets_blocked", GINT_TO_POINTER(FALSE));
4887             sp_dcc_build_presets_list (holder);
4889             g_signal_connect(G_OBJECT(act1), "changed", G_CALLBACK(sp_ddc_change_profile), holder);
4890             gtk_action_group_add_action(mainActions, GTK_ACTION(act1));
4891         }
4892     }
4896 //########################
4897 //##    Circle / Arc    ##
4898 //########################
4900 static void sp_arctb_sensitivize( GObject *tbl, double v1, double v2 )
4902     GtkAction *ocb = GTK_ACTION( g_object_get_data( tbl, "open_action" ) );
4903     GtkAction *make_whole = GTK_ACTION( g_object_get_data( tbl, "make_whole" ) );
4905     if (v1 == 0 && v2 == 0) {
4906         if (g_object_get_data( tbl, "single" )) { // only for a single selected ellipse (for now)
4907             gtk_action_set_sensitive( ocb, FALSE );
4908             gtk_action_set_sensitive( make_whole, FALSE );
4909         }
4910     } else {
4911         gtk_action_set_sensitive( ocb, TRUE );
4912         gtk_action_set_sensitive( make_whole, TRUE );
4913     }
4916 static void
4917 sp_arctb_startend_value_changed(GtkAdjustment *adj, GObject *tbl, gchar const *value_name, gchar const *other_name)
4919     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4921     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4922         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4923         prefs->setDouble(Glib::ustring("/tools/shapes/arc") + value_name, (adj->value * M_PI)/ 180);
4924     }
4926     // quit if run by the attr_changed listener
4927     if (g_object_get_data( tbl, "freeze" )) {
4928         return;
4929     }
4931     // in turn, prevent listener from responding
4932     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
4934     gchar* namespaced_name = g_strconcat("sodipodi:", value_name, NULL);
4936     bool modmade = false;
4937     for (GSList const *items = sp_desktop_selection(desktop)->itemList();
4938          items != NULL;
4939          items = items->next)
4940     {
4941         SPItem *item = SP_ITEM(items->data);
4943         if (SP_IS_ARC(item) && SP_IS_GENERICELLIPSE(item)) {
4945             SPGenericEllipse *ge = SP_GENERICELLIPSE(item);
4946             SPArc *arc = SP_ARC(item);
4948             if (!strcmp(value_name, "start"))
4949                 ge->start = (adj->value * M_PI)/ 180;
4950             else
4951                 ge->end = (adj->value * M_PI)/ 180;
4953             sp_genericellipse_normalize(ge);
4954             ((SPObject *)arc)->updateRepr();
4955             ((SPObject *)arc)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
4957             modmade = true;
4958         }
4959     }
4961     g_free(namespaced_name);
4963     GtkAdjustment *other = GTK_ADJUSTMENT( g_object_get_data( tbl, other_name ) );
4965     sp_arctb_sensitivize( tbl, adj->value, other->value );
4967     if (modmade) {
4968         sp_document_maybe_done(sp_desktop_document(desktop), value_name, SP_VERB_CONTEXT_ARC,
4969                                    _("Arc: Change start/end"));
4970     }
4972     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
4976 static void sp_arctb_start_value_changed(GtkAdjustment *adj,  GObject *tbl)
4978     sp_arctb_startend_value_changed(adj,  tbl, "start", "end");
4981 static void sp_arctb_end_value_changed(GtkAdjustment *adj, GObject *tbl)
4983     sp_arctb_startend_value_changed(adj,  tbl, "end", "start");
4987 static void sp_arctb_open_state_changed( EgeSelectOneAction *act, GObject *tbl )
4989     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
4990     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
4991         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
4992         prefs->setBool("/tools/shapes/arc/open", ege_select_one_action_get_active(act) != 0);
4993     }
4995     // quit if run by the attr_changed listener
4996     if (g_object_get_data( tbl, "freeze" )) {
4997         return;
4998     }
5000     // in turn, prevent listener from responding
5001     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5003     bool modmade = false;
5005     if ( ege_select_one_action_get_active(act) != 0 ) {
5006         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5007              items != NULL;
5008              items = items->next)
5009         {
5010             if (SP_IS_ARC((SPItem *) items->data)) {
5011                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5012                 repr->setAttribute("sodipodi:open", "true");
5013                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5014                 modmade = true;
5015             }
5016         }
5017     } else {
5018         for (GSList const *items = sp_desktop_selection(desktop)->itemList();
5019              items != NULL;
5020              items = items->next)
5021         {
5022             if (SP_IS_ARC((SPItem *) items->data))    {
5023                 Inkscape::XML::Node *repr = SP_OBJECT_REPR((SPItem *) items->data);
5024                 repr->setAttribute("sodipodi:open", NULL);
5025                 SP_OBJECT((SPItem *) items->data)->updateRepr();
5026                 modmade = true;
5027             }
5028         }
5029     }
5031     if (modmade) {
5032         sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_ARC,
5033                                    _("Arc: Change open/closed"));
5034     }
5036     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5039 static void sp_arctb_defaults(GtkWidget *, GObject *obj)
5041     GtkAdjustment *adj;
5042     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "start") );
5043     gtk_adjustment_set_value(adj, 0.0);
5044     gtk_adjustment_value_changed(adj);
5046     adj = GTK_ADJUSTMENT( g_object_get_data(obj, "end") );
5047     gtk_adjustment_set_value(adj, 0.0);
5048     gtk_adjustment_value_changed(adj);
5050     spinbutton_defocus( GTK_OBJECT(obj) );
5053 static void arc_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const */*name*/,
5054                                       gchar const */*old_value*/, gchar const */*new_value*/,
5055                                       bool /*is_interactive*/, gpointer data)
5057     GObject *tbl = G_OBJECT(data);
5059     // quit if run by the _changed callbacks
5060     if (g_object_get_data( tbl, "freeze" )) {
5061         return;
5062     }
5064     // in turn, prevent callbacks from responding
5065     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5067     gdouble start = sp_repr_get_double_attribute(repr, "sodipodi:start", 0.0);
5068     gdouble end = sp_repr_get_double_attribute(repr, "sodipodi:end", 0.0);
5070     GtkAdjustment *adj1,*adj2;
5071     adj1 = GTK_ADJUSTMENT( g_object_get_data( tbl, "start" ) );
5072     gtk_adjustment_set_value(adj1, mod360((start * 180)/M_PI));
5073     adj2 = GTK_ADJUSTMENT( g_object_get_data( tbl, "end" ) );
5074     gtk_adjustment_set_value(adj2, mod360((end * 180)/M_PI));
5076     sp_arctb_sensitivize( tbl, adj1->value, adj2->value );
5078     char const *openstr = NULL;
5079     openstr = repr->attribute("sodipodi:open");
5080     EgeSelectOneAction *ocb = EGE_SELECT_ONE_ACTION( g_object_get_data( tbl, "open_action" ) );
5082     if (openstr) {
5083         ege_select_one_action_set_active( ocb, 1 );
5084     } else {
5085         ege_select_one_action_set_active( ocb, 0 );
5086     }
5088     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5091 static Inkscape::XML::NodeEventVector arc_tb_repr_events = {
5092     NULL, /* child_added */
5093     NULL, /* child_removed */
5094     arc_tb_event_attr_changed,
5095     NULL, /* content_changed */
5096     NULL  /* order_changed */
5097 };
5100 static void sp_arc_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
5102     int n_selected = 0;
5103     Inkscape::XML::Node *repr = NULL;
5105     purge_repr_listener( tbl, tbl );
5107     for (GSList const *items = selection->itemList();
5108          items != NULL;
5109          items = items->next)
5110     {
5111         if (SP_IS_ARC((SPItem *) items->data)) {
5112             n_selected++;
5113             repr = SP_OBJECT_REPR((SPItem *) items->data);
5114         }
5115     }
5117     EgeOutputAction* act = EGE_OUTPUT_ACTION( g_object_get_data( tbl, "mode_action" ) );
5119     g_object_set_data( tbl, "single", GINT_TO_POINTER(FALSE) );
5120     if (n_selected == 0) {
5121         g_object_set( G_OBJECT(act), "label", _("<b>New:</b>"), NULL );
5122     } else if (n_selected == 1) {
5123         g_object_set_data( tbl, "single", GINT_TO_POINTER(TRUE) );
5124         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5126         if (repr) {
5127             g_object_set_data( tbl, "repr", repr );
5128             Inkscape::GC::anchor(repr);
5129             sp_repr_add_listener(repr, &arc_tb_repr_events, tbl);
5130             sp_repr_synthesize_events(repr, &arc_tb_repr_events, tbl);
5131         }
5132     } else {
5133         // FIXME: implement averaging of all parameters for multiple selected
5134         //gtk_label_set_markup(GTK_LABEL(l), _("<b>Average:</b>"));
5135         g_object_set( G_OBJECT(act), "label", _("<b>Change:</b>"), NULL );
5136         sp_arctb_sensitivize( tbl, 1, 0 );
5137     }
5141 static void sp_arc_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5143     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5145     EgeAdjustmentAction* eact = 0;
5146     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
5149     {
5150         EgeOutputAction* act = ege_output_action_new( "ArcStateAction", _("<b>New:</b>"), "", 0 );
5151         ege_output_action_set_use_markup( act, TRUE );
5152         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5153         g_object_set_data( holder, "mode_action", act );
5154     }
5156     /* Start */
5157     {
5158         eact = create_adjustment_action( "ArcStartAction",
5159                                          _("Start"), _("Start:"),
5160                                          _("The angle (in degrees) from the horizontal to the arc's start point"),
5161                                          "/tools/shapes/arc/start", 0.0,
5162                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE, "altx-arc",
5163                                          -360.0, 360.0, 1.0, 10.0,
5164                                          0, 0, 0,
5165                                          sp_arctb_start_value_changed);
5166         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5167     }
5169     /* End */
5170     {
5171         eact = create_adjustment_action( "ArcEndAction",
5172                                          _("End"), _("End:"),
5173                                          _("The angle (in degrees) from the horizontal to the arc's end point"),
5174                                          "/tools/shapes/arc/end", 0.0,
5175                                          GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, FALSE, NULL,
5176                                          -360.0, 360.0, 1.0, 10.0,
5177                                          0, 0, 0,
5178                                          sp_arctb_end_value_changed);
5179         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5180     }
5182     /* Segments / Pie checkbox */
5183     {
5184         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5186         GtkTreeIter iter;
5187         gtk_list_store_append( model, &iter );
5188         gtk_list_store_set( model, &iter,
5189                             0, _("Closed arc"),
5190                             1, _("Switch to segment (closed shape with two radii)"),
5191                             2, INKSCAPE_ICON_DRAW_ELLIPSE_SEGMENT,
5192                             -1 );
5194         gtk_list_store_append( model, &iter );
5195         gtk_list_store_set( model, &iter,
5196                             0, _("Open Arc"),
5197                             1, _("Switch to arc (unclosed shape)"),
5198                             2, INKSCAPE_ICON_DRAW_ELLIPSE_ARC,
5199                             -1 );
5201         EgeSelectOneAction* act = ege_select_one_action_new( "ArcOpenAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5202         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5203         g_object_set_data( holder, "open_action", act );
5205         ege_select_one_action_set_appearance( act, "full" );
5206         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5207         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5208         ege_select_one_action_set_icon_column( act, 2 );
5209         ege_select_one_action_set_icon_size( act, secondarySize );
5210         ege_select_one_action_set_tooltip_column( act, 1  );
5212         bool isClosed = !prefs->getBool("/tools/shapes/arc/open", false);
5213         ege_select_one_action_set_active( act, isClosed ? 0 : 1 );
5214         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_arctb_open_state_changed), holder );
5215     }
5217     /* Make Whole */
5218     {
5219         InkAction* inky = ink_action_new( "ArcResetAction",
5220                                           _("Make whole"),
5221                                           _("Make the shape a whole ellipse, not arc or segment"),
5222                                           INKSCAPE_ICON_DRAW_ELLIPSE_WHOLE,
5223                                           secondarySize );
5224         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_arctb_defaults), holder );
5225         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
5226         gtk_action_set_sensitive( GTK_ACTION(inky), TRUE );
5227         g_object_set_data( holder, "make_whole", inky );
5228     }
5230     g_object_set_data( G_OBJECT(holder), "single", GINT_TO_POINTER(TRUE) );
5231     // sensitivize make whole and open checkbox
5232     {
5233         GtkAdjustment *adj1 = GTK_ADJUSTMENT( g_object_get_data( holder, "start" ) );
5234         GtkAdjustment *adj2 = GTK_ADJUSTMENT( g_object_get_data( holder, "end" ) );
5235         sp_arctb_sensitivize( holder, adj1->value, adj2->value );
5236     }
5239     sigc::connection *connection = new sigc::connection(
5240         sp_desktop_selection(desktop)->connectChanged(sigc::bind(sigc::ptr_fun(sp_arc_toolbox_selection_changed), (GObject *)holder))
5241         );
5242     g_signal_connect( holder, "destroy", G_CALLBACK(delete_connection), connection );
5243     g_signal_connect( holder, "destroy", G_CALLBACK(purge_repr_listener), holder );
5249 // toggle button callbacks and updaters
5251 //########################
5252 //##      Dropper       ##
5253 //########################
5255 static void toggle_dropper_pick_alpha( GtkToggleAction* act, gpointer tbl ) {
5256     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5257     prefs->setInt( "/tools/dropper/pick", gtk_toggle_action_get_active( act ) );
5258     GtkAction* set_action = GTK_ACTION( g_object_get_data(G_OBJECT(tbl), "set_action") );
5259     if ( set_action ) {
5260         if ( gtk_toggle_action_get_active( act ) ) {
5261             gtk_action_set_sensitive( set_action, TRUE );
5262         } else {
5263             gtk_action_set_sensitive( set_action, FALSE );
5264         }
5265     }
5267     spinbutton_defocus(GTK_OBJECT(tbl));
5270 static void toggle_dropper_set_alpha( GtkToggleAction* act, gpointer tbl ) {
5271     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5272     prefs->setBool( "/tools/dropper/setalpha", gtk_toggle_action_get_active( act ) );
5273     spinbutton_defocus(GTK_OBJECT(tbl));
5277 /**
5278  * Dropper auxiliary toolbar construction and setup.
5279  *
5280  * TODO: Would like to add swatch of current color.
5281  * TODO: Add queue of last 5 or so colors selected with new swatches so that
5282  *       can drag and drop places. Will provide a nice mixing palette.
5283  */
5284 static void sp_dropper_toolbox_prep(SPDesktop */*desktop*/, GtkActionGroup* mainActions, GObject* holder)
5286     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5287     gint pickAlpha = prefs->getInt( "/tools/dropper/pick", 1 );
5289     {
5290         EgeOutputAction* act = ege_output_action_new( "DropperOpacityAction", _("Opacity:"), "", 0 );
5291         ege_output_action_set_use_markup( act, TRUE );
5292         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5293     }
5295     {
5296         InkToggleAction* act = ink_toggle_action_new( "DropperPickAlphaAction",
5297                                                       _("Pick opacity"),
5298                                                       _("Pick both the color and the alpha (transparency) under cursor; otherwise, pick only the visible color premultiplied by alpha"),
5299                                                       NULL,
5300                                                       Inkscape::ICON_SIZE_DECORATION );
5301         g_object_set( act, "short_label", _("Pick"), NULL );
5302         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5303         g_object_set_data( holder, "pick_action", act );
5304         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), pickAlpha );
5305         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_pick_alpha), holder );
5306     }
5308     {
5309         InkToggleAction* act = ink_toggle_action_new( "DropperSetAlphaAction",
5310                                                       _("Assign opacity"),
5311                                                       _("If alpha was picked, assign it to selection as fill or stroke transparency"),
5312                                                       NULL,
5313                                                       Inkscape::ICON_SIZE_DECORATION );
5314         g_object_set( act, "short_label", _("Assign"), NULL );
5315         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5316         g_object_set_data( holder, "set_action", act );
5317         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/dropper/setalpha", true) );
5318         // make sure it's disabled if we're not picking alpha
5319         gtk_action_set_sensitive( GTK_ACTION(act), pickAlpha );
5320         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_dropper_set_alpha), holder );
5321     }
5325 //########################
5326 //##      LPETool       ##
5327 //########################
5329 // the subtools from which the toolbar is built automatically are listed in lpe-tool-context.h
5331 // this is called when the mode is changed via the toolbar (i.e., one of the subtool buttons is pressed)
5332 static void sp_lpetool_mode_changed(EgeSelectOneAction *act, GObject *tbl)
5334     using namespace Inkscape::LivePathEffect;
5336     SPDesktop *desktop = (SPDesktop *) g_object_get_data(tbl, "desktop");
5337     SPEventContext *ec = desktop->event_context;
5338     if (!SP_IS_LPETOOL_CONTEXT(ec)) {
5339         return;
5340     }
5342     // only take action if run by the attr_changed listener
5343     if (!g_object_get_data(tbl, "freeze")) {
5344         // in turn, prevent listener from responding
5345         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5347         gint mode = ege_select_one_action_get_active(act);
5348         EffectType type = lpesubtools[mode];
5350         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5351         bool success = lpetool_try_construction(lc, type);
5352         if (success) {
5353             // since the construction was already performed, we set the state back to inactive
5354             ege_select_one_action_set_active(act, 0);
5355             mode = 0;
5356         } else {
5357             // switch to the chosen subtool
5358             SP_LPETOOL_CONTEXT(desktop->event_context)->mode = type;
5359         }
5361         if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5362             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5363             prefs->setInt( "/tools/lpetool/mode", mode );
5364         }
5366         g_object_set_data(tbl, "freeze", GINT_TO_POINTER(FALSE));
5367     }
5370 void sp_lpetool_toolbox_sel_modified(Inkscape::Selection *selection, guint /*flags*/, GObject */*tbl*/)
5372     SPEventContext *ec = selection->desktop()->event_context;
5373     if (!SP_IS_LPETOOL_CONTEXT(ec))
5374         return;
5376     lpetool_update_measuring_items(SP_LPETOOL_CONTEXT(ec));
5379 void
5380 sp_lpetool_toolbox_sel_changed(Inkscape::Selection *selection, GObject *tbl)
5382     using namespace Inkscape::LivePathEffect;
5383     SPEventContext *ec = selection->desktop()->event_context;
5384     if (!SP_IS_LPETOOL_CONTEXT(ec))
5385         return;
5386     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(ec);
5388     lpetool_delete_measuring_items(lc);
5389     lpetool_create_measuring_items(lc, selection);
5391     // activate line segment combo box if a single item with LPELineSegment is selected
5392     GtkAction* w = GTK_ACTION(g_object_get_data(tbl, "lpetool_line_segment_action"));
5393     SPItem *item = selection->singleItem();
5394     if (item && SP_IS_LPE_ITEM(item) && lpetool_item_has_construction(lc, item)) {
5395         SPLPEItem *lpeitem = SP_LPE_ITEM(item);
5396         Effect* lpe = sp_lpe_item_get_current_lpe(lpeitem);
5397         if (lpe && lpe->effectType() == LINE_SEGMENT) {
5398             LPELineSegment *lpels = static_cast<LPELineSegment*>(lpe);
5399             g_object_set_data(tbl, "currentlpe", lpe);
5400             g_object_set_data(tbl, "currentlpeitem", lpeitem);
5401             gtk_action_set_sensitive(w, TRUE);
5402             ege_select_one_action_set_active(EGE_SELECT_ONE_ACTION(w), lpels->end_type.get_value());
5403         } else {
5404             g_object_set_data(tbl, "currentlpe", NULL);
5405             g_object_set_data(tbl, "currentlpeitem", NULL);
5406             gtk_action_set_sensitive(w, FALSE);
5407         }
5408     } else {
5409         g_object_set_data(tbl, "currentlpe", NULL);
5410         g_object_set_data(tbl, "currentlpeitem", NULL);
5411         gtk_action_set_sensitive(w, FALSE);
5412     }
5415 static void
5416 lpetool_toggle_show_bbox (GtkToggleAction *act, gpointer data) {
5417     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5418     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5420     bool show = gtk_toggle_action_get_active( act );
5421     prefs->setBool("/tools/lpetool/show_bbox",  show);
5423     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5424         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5425         lpetool_context_reset_limiting_bbox(lc);
5426     }
5429 static void
5430 lpetool_toggle_show_measuring_info (GtkToggleAction *act, GObject *tbl) {
5431     SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(tbl, "desktop"));
5432     if (!tools_isactive(desktop, TOOLS_LPETOOL))
5433         return;
5435     GtkAction *unitact = static_cast<GtkAction*>(g_object_get_data(tbl, "lpetool_units_action"));
5436     SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5437     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5438         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5439         bool show = gtk_toggle_action_get_active( act );
5440         prefs->setBool("/tools/lpetool/show_measuring_info",  show);
5441         lpetool_show_measuring_info(lc, show);
5442         gtk_action_set_sensitive(GTK_ACTION(unitact), show);
5443     }
5446 static void lpetool_unit_changed(GtkAction* /*act*/, GObject* tbl) {
5447     UnitTracker* tracker = reinterpret_cast<UnitTracker*>(g_object_get_data(tbl, "tracker"));
5448     SPUnit const *unit = tracker->getActiveUnit();
5449     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5450     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5452     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5453     if (SP_IS_LPETOOL_CONTEXT(desktop->event_context)) {
5454         SPLPEToolContext *lc = SP_LPETOOL_CONTEXT(desktop->event_context);
5455         lpetool_delete_measuring_items(lc);
5456         lpetool_create_measuring_items(lc);
5457     }
5460 static void
5461 lpetool_toggle_set_bbox (GtkToggleAction *act, gpointer data) {
5462     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5463     Inkscape::Selection *selection = desktop->selection;
5465     Geom::OptRect bbox = selection->bounds();
5467     if (bbox) {
5468         Geom::Point A(bbox->min());
5469         Geom::Point B(bbox->max());
5471         A *= desktop->doc2dt();
5472         B *= desktop->doc2dt();
5474         // TODO: should we provide a way to store points in prefs?
5475         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5476         prefs->setDouble("/tools/lpetool/bbox_upperleftx", A[Geom::X]);
5477         prefs->setDouble("/tools/lpetool/bbox_upperlefty", A[Geom::Y]);
5478         prefs->setDouble("/tools/lpetool/bbox_lowerrightx", B[Geom::X]);
5479         prefs->setDouble("/tools/lpetool/bbox_lowerrighty", B[Geom::Y]);
5481         lpetool_context_reset_limiting_bbox(SP_LPETOOL_CONTEXT(desktop->event_context));
5482     }
5484     gtk_toggle_action_set_active(act, false);
5487 static void
5488 sp_line_segment_build_list(GObject *tbl)
5490     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(TRUE));
5492     EgeSelectOneAction* selector = static_cast<EgeSelectOneAction *>(g_object_get_data(tbl, "lpetool_line_segment_action"));
5493     GtkListStore* model = GTK_LIST_STORE(ege_select_one_action_get_model(selector));
5494     gtk_list_store_clear (model);
5496     // TODO: we add the entries of rht combo box manually; later this should be done automatically
5497     {
5498         GtkTreeIter iter;
5499         gtk_list_store_append( model, &iter );
5500         gtk_list_store_set( model, &iter, 0, _("Closed"), 1, 0, -1 );
5501         gtk_list_store_append( model, &iter );
5502         gtk_list_store_set( model, &iter, 0, _("Open start"), 1, 1, -1 );
5503         gtk_list_store_append( model, &iter );
5504         gtk_list_store_set( model, &iter, 0, _("Open end"), 1, 2, -1 );
5505         gtk_list_store_append( model, &iter );
5506         gtk_list_store_set( model, &iter, 0, _("Open both"), 1, 3, -1 );
5507     }
5509     g_object_set_data(tbl, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5512 static void
5513 sp_lpetool_change_line_segment_type(EgeSelectOneAction* act, GObject* tbl) {
5514     using namespace Inkscape::LivePathEffect;
5516     // quit if run by the attr_changed listener
5517     if (g_object_get_data(tbl, "freeze")) {
5518         return;
5519     }
5521     // in turn, prevent listener from responding
5522     g_object_set_data(tbl, "freeze", GINT_TO_POINTER(TRUE));
5524     LPELineSegment *lpe = static_cast<LPELineSegment *>(g_object_get_data(tbl, "currentlpe"));
5525     SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5526     if (lpeitem) {
5527         SPLPEItem *lpeitem = static_cast<SPLPEItem *>(g_object_get_data(tbl, "currentlpeitem"));
5528         lpe->end_type.param_set_value(static_cast<Inkscape::LivePathEffect::EndType>(ege_select_one_action_get_active(act)));
5529         sp_lpe_item_update_patheffect(lpeitem, true, true);
5530     }
5532     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5535 static void
5536 lpetool_open_lpe_dialog (GtkToggleAction *act, gpointer data) {
5537     SPDesktop *desktop = static_cast<SPDesktop *>(data);
5539     if (tools_isactive(desktop, TOOLS_LPETOOL)) {
5540         sp_action_perform(Inkscape::Verb::get(SP_VERB_DIALOG_LIVE_PATH_EFFECT)->get_action(desktop), NULL);
5541     }
5542     gtk_toggle_action_set_active(act, false);
5545 static void sp_lpetool_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5547     UnitTracker* tracker = new UnitTracker(SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE);
5548     tracker->setActiveUnit(sp_desktop_namedview(desktop)->doc_units);
5549     g_object_set_data(holder, "tracker", tracker);
5550     SPUnit const *unit = tracker->getActiveUnit();
5552     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5553     prefs->setInt("/tools/lpetool/unitid", unit->unit_id);
5555     /** Automatically create a list of LPEs that get added to the toolbar **/
5556     {
5557         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5559         GtkTreeIter iter;
5561         // the first toggle button represents the state that no subtool is active (remove this when
5562         // this can be modeled by EgeSelectOneAction or some other action)
5563         gtk_list_store_append( model, &iter );
5564         gtk_list_store_set( model, &iter,
5565                             0, _("All inactive"),
5566                             1, _("No geometric tool is active"),
5567                             2, _("all_inactive"),
5568                             -1 );
5570         Inkscape::LivePathEffect::EffectType type;
5571         for (int i = 1; i < num_subtools; ++i) { // we start with i = 1 because INVALID_LPE was already added
5572             type =  lpesubtools[i];
5573             gtk_list_store_append( model, &iter );
5574             gtk_list_store_set( model, &iter,
5575                                 0, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5576                                 1, Inkscape::LivePathEffect::LPETypeConverter.get_label(type).c_str(),
5577                                 2, Inkscape::LivePathEffect::LPETypeConverter.get_key(type).c_str(),
5578                                 -1 );
5579         }
5581         EgeSelectOneAction* act = ege_select_one_action_new( "LPEToolModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5582         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5583         g_object_set_data( holder, "lpetool_mode_action", act );
5585         ege_select_one_action_set_appearance( act, "full" );
5586         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5587         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5588         ege_select_one_action_set_icon_column( act, 2 );
5589         ege_select_one_action_set_tooltip_column( act, 1  );
5591         gint lpeToolMode = prefs->getInt("/tools/lpetool/mode", 0);
5592         ege_select_one_action_set_active( act, lpeToolMode );
5593         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_mode_changed), holder );
5594     }
5596     /* Show limiting bounding box */
5597     {
5598         InkToggleAction* act = ink_toggle_action_new( "LPEShowBBoxAction",
5599                                                       _("Show limiting bounding box"),
5600                                                       _("Show bounding box (used to cut infinite lines)"),
5601                                                       "lpetool_show_bbox",
5602                                                       Inkscape::ICON_SIZE_DECORATION );
5603         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5604         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_bbox), desktop );
5605         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_bbox", true ) );
5606     }
5608     /* Set limiting bounding box to bbox of current selection */
5609     {
5610         InkToggleAction* act = ink_toggle_action_new( "LPEBBoxFromSelectionAction",
5611                                                       _("Get limiting bounding box from selection"),
5612                                                       _("Set limiting bounding box (used to cut infinite lines) to the bounding box of current selection"),
5613                                                       "lpetool_set_bbox",
5614                                                       Inkscape::ICON_SIZE_DECORATION );
5615         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5616         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_set_bbox), desktop );
5617         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5618     }
5621     /* Combo box to choose line segment type */
5622     {
5623         GtkListStore* model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
5624         EgeSelectOneAction* act = ege_select_one_action_new ("LPELineSegmentAction", "" , (_("Choose a line segment type")), NULL, GTK_TREE_MODEL(model));
5625         ege_select_one_action_set_appearance (act, "compact");
5626         g_object_set_data (holder, "lpetool_line_segment_action", act );
5628         g_object_set_data(holder, "line_segment_list_blocked", GINT_TO_POINTER(FALSE));
5630         sp_line_segment_build_list (holder);
5632         g_signal_connect(G_OBJECT(act), "changed", G_CALLBACK(sp_lpetool_change_line_segment_type), holder);
5633         gtk_action_set_sensitive( GTK_ACTION(act), FALSE );
5634         gtk_action_group_add_action(mainActions, GTK_ACTION(act));
5635     }
5637     /* Display measuring info for selected items */
5638     {
5639         InkToggleAction* act = ink_toggle_action_new( "LPEMeasuringAction",
5640                                                       _("Display measuring info"),
5641                                                       _("Display measuring info for selected items"),
5642                                                       "lpetool_measuring_info",
5643                                                       Inkscape::ICON_SIZE_DECORATION );
5644         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5645         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_toggle_show_measuring_info), holder );
5646         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs->getBool( "/tools/lpetool/show_measuring_info", true ) );
5647     }
5649     // add the units menu
5650     {
5651         GtkAction* act = tracker->createAction( "LPEToolUnitsAction", _("Units"), ("") );
5652         gtk_action_group_add_action( mainActions, act );
5653         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(lpetool_unit_changed), (GObject*)holder );
5654         g_object_set_data(holder, "lpetool_units_action", act);
5655         gtk_action_set_sensitive(act, prefs->getBool("/tools/lpetool/show_measuring_info", true));
5656     }
5658     /* Open LPE dialog (to adapt parameters numerically) */
5659     {
5660         InkToggleAction* act = ink_toggle_action_new( "LPEOpenLPEDialogAction",
5661                                                       _("Open LPE dialog"),
5662                                                       _("Open LPE dialog (to adapt parameters numerically)"),
5663                                                       "lpetool_open_lpe_dialog",
5664                                                       Inkscape::ICON_SIZE_DECORATION );
5665         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
5666         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(lpetool_open_lpe_dialog), desktop );
5667         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), FALSE );
5668     }
5670     //watch selection
5671     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISNodeToolbox");
5673     sigc::connection *c_selection_modified =
5674         new sigc::connection (sp_desktop_selection (desktop)->connectModified
5675                               (sigc::bind (sigc::ptr_fun (sp_lpetool_toolbox_sel_modified), (GObject*)holder)));
5676     pool->add_connection ("selection-modified", c_selection_modified);
5678     sigc::connection *c_selection_changed =
5679         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
5680                               (sigc::bind (sigc::ptr_fun(sp_lpetool_toolbox_sel_changed), (GObject*)holder)));
5681     pool->add_connection ("selection-changed", c_selection_changed);
5684 //########################
5685 //##       Eraser       ##
5686 //########################
5688 static void sp_erc_width_value_changed( GtkAdjustment *adj, GObject *tbl )
5690     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5691     prefs->setDouble( "/tools/eraser/width", adj->value * 0.01 );
5692     update_presets_list(tbl);
5695 static void sp_erasertb_mode_changed( EgeSelectOneAction *act, GObject *tbl )
5697     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
5698     bool eraserMode = ege_select_one_action_get_active( act ) != 0;
5699     if (sp_document_get_undo_sensitive(sp_desktop_document(desktop))) {
5700         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5701         prefs->setBool( "/tools/eraser/mode", eraserMode );
5702     }
5704     // only take action if run by the attr_changed listener
5705     if (!g_object_get_data( tbl, "freeze" )) {
5706         // in turn, prevent listener from responding
5707         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
5709         if ( eraserMode != 0 ) {
5710         } else {
5711         }
5712         // TODO finish implementation
5714         g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
5715     }
5718 static void sp_eraser_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
5720     {
5721         /* Width */
5722         gchar const* labels[] = {_("(hairline)"), 0, 0, 0, _("(default)"), 0, 0, 0, 0, _("(broad stroke)")};
5723         gdouble values[] = {1, 3, 5, 10, 15, 20, 30, 50, 75, 100};
5724         EgeAdjustmentAction *eact = create_adjustment_action( "EraserWidthAction",
5725                                                               _("Pen Width"), _("Width:"),
5726                                                               _("The width of the eraser pen (relative to the visible canvas area)"),
5727                                                               "/tools/eraser/width", 15,
5728                                                               GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-eraser",
5729                                                               1, 100, 1.0, 0.0,
5730                                                               labels, values, G_N_ELEMENTS(labels),
5731                                                               sp_erc_width_value_changed,  0.01, 0, 100 );
5732         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
5733         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
5734         gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
5735     }
5737     {
5738         GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
5740         GtkTreeIter iter;
5741         gtk_list_store_append( model, &iter );
5742         gtk_list_store_set( model, &iter,
5743                             0, _("Delete"),
5744                             1, _("Delete objects touched by the eraser"),
5745                             2, INKSCAPE_ICON_DRAW_ERASER_DELETE_OBJECTS,
5746                             -1 );
5748         gtk_list_store_append( model, &iter );
5749         gtk_list_store_set( model, &iter,
5750                             0, _("Cut"),
5751                             1, _("Cut out from objects"),
5752                             2, INKSCAPE_ICON_PATH_DIFFERENCE,
5753                             -1 );
5755         EgeSelectOneAction* act = ege_select_one_action_new( "EraserModeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
5756         gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
5757         g_object_set_data( holder, "eraser_mode_action", act );
5759         ege_select_one_action_set_appearance( act, "full" );
5760         ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
5761         g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
5762         ege_select_one_action_set_icon_column( act, 2 );
5763         ege_select_one_action_set_tooltip_column( act, 1  );
5765         /// @todo Convert to boolean?
5766         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5767         gint eraserMode = prefs->getBool("/tools/eraser/mode") ? 1 : 0;
5768         ege_select_one_action_set_active( act, eraserMode );
5769         g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(sp_erasertb_mode_changed), holder );
5770     }
5774 //########################
5775 //##    Text Toolbox    ##
5776 //########################
5777 /*
5778 static void
5779 sp_text_letter_changed(GtkAdjustment *adj, GtkWidget *tbl)
5781     //Call back for letter sizing spinbutton
5784 static void
5785 sp_text_line_changed(GtkAdjustment *adj, GtkWidget *tbl)
5787     //Call back for line height spinbutton
5790 static void
5791 sp_text_horiz_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5793     //Call back for horizontal kerning spinbutton
5796 static void
5797 sp_text_vert_kern_changed(GtkAdjustment *adj, GtkWidget *tbl)
5799     //Call back for vertical kerning spinbutton
5802 static void
5803 sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
5805     //Call back for letter rotation spinbutton
5806 }*/
5808 namespace {
5810 bool popdown_visible = false;
5811 bool popdown_hasfocus = false;
5813 void
5814 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
5816     SPStyle *query =
5817         sp_style_new (SP_ACTIVE_DOCUMENT);
5819 //    int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
5821     int result_family =
5822         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
5824     int result_style =
5825         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
5827     int result_numbers =
5828         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
5830     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
5832     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
5833     if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
5834         // there are no texts in selection, read from prefs
5836             sp_style_read_from_prefs(query, "/tools/text");
5838             if (g_object_get_data(tbl, "text_style_from_prefs")) {
5839             // do not reset the toolbar style from prefs if we already did it last time
5840             sp_style_unref(query);
5841             return;
5842         }
5843         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(TRUE));
5844     } else {
5845         g_object_set_data(tbl, "text_style_from_prefs", GINT_TO_POINTER(FALSE));
5846     }
5848     if (query->text)
5849     {
5850         if (result_family == QUERY_STYLE_MULTIPLE_DIFFERENT) {
5851             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5852             gtk_entry_set_text (GTK_ENTRY (entry), "");
5854         } else if (query->text->font_specification.value || query->text->font_family.value) {
5856             GtkWidget *entry = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "family-entry"));
5858             // Get the font that corresponds
5859             Glib::ustring familyName;
5861             font_instance * font = font_factory::Default()->FaceFromStyle(query);
5862             if (font) {
5863                 familyName = font_factory::Default()->GetUIFamilyString(font->descr);
5864                 font->Unref();
5865                 font = NULL;
5866             }
5868             gtk_entry_set_text (GTK_ENTRY (entry), familyName.c_str());
5870             Gtk::TreePath path;
5871             try {
5872                 path = Inkscape::FontLister::get_instance()->get_row_for_font (familyName);
5873             } catch (...) {
5874                 g_warning("Family name %s does not have an entry in the font lister.", familyName.c_str());
5875                 sp_style_unref(query);
5876                 return;
5877             }
5879             GtkTreeSelection *tselection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
5880             GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
5882             g_object_set_data (G_OBJECT (tselection), "block", gpointer(1));
5884             gtk_tree_selection_select_path (tselection, path.gobj());
5885             gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
5887             g_object_set_data (G_OBJECT (tselection), "block", gpointer(0));
5888         }
5890         //Size
5891         {
5892             GtkWidget *cbox = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "combo-box-size"));
5893             gchar *const str = g_strdup_printf("%.5g", query->font_size.computed);
5894             g_object_set_data(tbl, "size-block", gpointer(1));
5895             gtk_entry_set_text(GTK_ENTRY(GTK_BIN(cbox)->child), str);
5896             g_object_set_data(tbl, "size-block", gpointer(0));
5897             g_free(str);
5898         }
5900         //Anchor
5901         if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY)
5902         {
5903             GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-fill"));
5904             g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5905             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5906             g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5907         }
5908         else
5909         {
5910             if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START)
5911             {
5912                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-start"));
5913                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5914                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5915                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5916             }
5917             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE)
5918             {
5919                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-middle"));
5920                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5921                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5922                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5923             }
5924             else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_END)
5925             {
5926                 GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "text-end"));
5927                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5928                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5929                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5930             }
5931         }
5933         //Style
5934         {
5935             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-bold"));
5937             gboolean active = gtk_toggle_button_get_active (button);
5938             gboolean check  = (query->font_weight.computed >= SP_CSS_FONT_WEIGHT_700);
5940             if (active != check)
5941             {
5942                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5943                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5944                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5945             }
5946         }
5948         {
5949             GtkToggleButton *button = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT (tbl), "style-italic"));
5951             gboolean active = gtk_toggle_button_get_active (button);
5952             gboolean check  = (query->font_style.computed != SP_CSS_FONT_STYLE_NORMAL);
5954             if (active != check)
5955             {
5956                 g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5957                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), check);
5958                 g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5959             }
5960         }
5962         //Orientation
5963         //locking both buttons, changing one affect all group (both)
5964         GtkWidget *button = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-horizontal"));
5965         g_object_set_data (G_OBJECT (button), "block", gpointer(1));
5967         GtkWidget *button1 = GTK_WIDGET (g_object_get_data (G_OBJECT (tbl), "orientation-vertical"));
5968         g_object_set_data (G_OBJECT (button1), "block", gpointer(1));
5970         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB)
5971         {
5972             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
5973         }
5974         else
5975         {
5976             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button1), TRUE);
5977         }
5978         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
5979         g_object_set_data (G_OBJECT (button1), "block", gpointer(0));
5980     }
5982     sp_style_unref(query);
5985 void
5986 sp_text_toolbox_selection_modified (Inkscape::Selection *selection, guint /*flags*/, GObject *tbl)
5988     sp_text_toolbox_selection_changed (selection, tbl);
5991 void
5992 sp_text_toolbox_subselection_changed (gpointer /*dragger*/, GObject *tbl)
5994     sp_text_toolbox_selection_changed (NULL, tbl);
5997 void
5998 sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
5999                                 GObject             *tbl)
6001     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
6002     GtkTreeModel *model = 0;
6003     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6004     GtkTreeIter   iter;
6005     char         *family = 0;
6007     gdk_pointer_ungrab (GDK_CURRENT_TIME);
6008     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6010     if ( !gtk_tree_selection_get_selected( selection, &model, &iter ) ) {
6011         return;
6012     }
6014     gtk_tree_model_get (model, &iter, 0, &family, -1);
6016     if (g_object_get_data (G_OBJECT (selection), "block"))
6017     {
6018         gtk_entry_set_text (GTK_ENTRY (entry), family);
6019         return;
6020     }
6022     gtk_entry_set_text (GTK_ENTRY (entry), family);
6024     SPStyle *query =
6025         sp_style_new (SP_ACTIVE_DOCUMENT);
6027     int result_fontspec =
6028         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6030     //font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6032     SPCSSAttr *css = sp_repr_css_attr_new ();
6035     // First try to get the font spec from the stored value
6036     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6038     if (fontSpec.empty()) {
6039         // Construct a new font specification if it does not yet exist
6040         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6041         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6042         fontFromStyle->Unref();
6043     }
6045     if (!fontSpec.empty()) {
6046         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
6047         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
6048             font_instance *font = font_factory::Default()->FaceFromFontSpecification(newFontSpec.c_str());
6049             if (font) {
6050                 sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6052                 // Set all the these just in case they were altered when finding the best
6053                 // match for the new family and old style...
6055                 gchar c[256];
6057                 font->Family(c, 256);
6058                 sp_repr_css_set_property (css, "font-family", c);
6060                 font->Attribute( "weight", c, 256);
6061                 sp_repr_css_set_property (css, "font-weight", c);
6063                 font->Attribute("style", c, 256);
6064                 sp_repr_css_set_property (css, "font-style", c);
6066                 font->Attribute("stretch", c, 256);
6067                 sp_repr_css_set_property (css, "font-stretch", c);
6069                 font->Attribute("variant", c, 256);
6070                 sp_repr_css_set_property (css, "font-variant", c);
6072                 font->Unref();
6073             }
6074         }
6075     }
6077     // If querying returned nothing, set the default style of the tool (for new texts)
6078     if (result_fontspec == QUERY_STYLE_NOTHING)
6079     {
6080         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6081         prefs->setStyle("/tools/text/style", css);
6082         sp_text_edit_dialog_default_set_insensitive (); //FIXME: Replace trough a verb
6083     }
6084     else
6085     {
6086         sp_desktop_set_style (desktop, css, true, true);
6087     }
6089     sp_style_unref(query);
6091     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6092                                    _("Text: Change font family"));
6093     sp_repr_css_attr_unref (css);
6094     g_free(family);
6095     gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6097     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6100 /* This is where execution comes when the contents of the font family box have been completed
6101    by the press of the return key */
6102 void
6103 sp_text_toolbox_family_entry_activate (GtkEntry     *entry,
6104                                        GObject      *tbl)
6106     const char *family = gtk_entry_get_text (entry);   // Fetch the requested font family
6108 // Try to match that to a known font. If not, then leave current font alone and remain focused on text box
6109     try {
6110         Gtk::TreePath path = Inkscape::FontLister::get_instance()->get_row_for_font (family);
6111         GtkTreeSelection *selection = GTK_TREE_SELECTION (g_object_get_data (G_OBJECT(tbl), "family-tree-selection"));
6112         GtkTreeView *treeview = GTK_TREE_VIEW (g_object_get_data (G_OBJECT(tbl), "family-tree-view"));
6113         gtk_tree_selection_select_path (selection, path.gobj());
6114         gtk_tree_view_scroll_to_cell (treeview, path.gobj(), NULL, TRUE, 0.5, 0.0);
6115         gtk_widget_hide (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6116     } catch (...) {
6117         if (family && strlen (family))
6118         {
6119             gtk_widget_show_all (GTK_WIDGET (g_object_get_data (G_OBJECT(tbl), "warning-image")));
6120         }
6121     }
6124 void
6125 sp_text_toolbox_anchoring_toggled (GtkRadioButton   *button,
6126                                    gpointer          data)
6128     if (g_object_get_data (G_OBJECT (button), "block")) return;
6129     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return;
6130     int prop = GPOINTER_TO_INT(data);
6132     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6133     SPCSSAttr *css = sp_repr_css_attr_new ();
6135     switch (prop)
6136     {
6137         case 0:
6138         {
6139             sp_repr_css_set_property (css, "text-anchor", "start");
6140             sp_repr_css_set_property (css, "text-align", "start");
6141             break;
6142         }
6143         case 1:
6144         {
6145             sp_repr_css_set_property (css, "text-anchor", "middle");
6146             sp_repr_css_set_property (css, "text-align", "center");
6147             break;
6148         }
6150         case 2:
6151         {
6152             sp_repr_css_set_property (css, "text-anchor", "end");
6153             sp_repr_css_set_property (css, "text-align", "end");
6154             break;
6155         }
6157         case 3:
6158         {
6159             sp_repr_css_set_property (css, "text-anchor", "start");
6160             sp_repr_css_set_property (css, "text-align", "justify");
6161             break;
6162         }
6163     }
6165     SPStyle *query =
6166         sp_style_new (SP_ACTIVE_DOCUMENT);
6167     int result_numbers =
6168         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6170     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6171     if (result_numbers == QUERY_STYLE_NOTHING)
6172     {
6173         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6174         prefs->setStyle("/tools/text/style", css);
6175     }
6177     sp_style_unref(query);
6179     sp_desktop_set_style (desktop, css, true, true);
6180     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6181                                    _("Text: Change alignment"));
6182     sp_repr_css_attr_unref (css);
6184     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6187 void
6188 sp_text_toolbox_style_toggled (GtkToggleButton  *button,
6189                                gpointer          data)
6191     if (g_object_get_data (G_OBJECT (button), "block")) return;
6193     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6194     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6195     int          prop       = GPOINTER_TO_INT(data);
6196     bool         active     = gtk_toggle_button_get_active (button);
6198     SPStyle *query =
6199         sp_style_new (SP_ACTIVE_DOCUMENT);
6201     int result_fontspec =
6202         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
6204     //int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
6205     //int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
6206     //int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6208     Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
6209     Glib::ustring newFontSpec = "";
6211     if (fontSpec.empty()) {
6212         // Construct a new font specification if it does not yet exist
6213         font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
6214         fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
6215         fontFromStyle->Unref();
6216     }
6218     switch (prop)
6219     {
6220         case 0:
6221         {
6222             if (!fontSpec.empty()) {
6223                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
6224             }
6225             if (fontSpec != newFontSpec) {
6226                 // Don't even set the bold if the font didn't exist on the system
6227                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
6228             }
6229             break;
6230         }
6232         case 1:
6233         {
6234             if (!fontSpec.empty()) {
6235                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
6236             }
6237             if (fontSpec != newFontSpec) {
6238                 // Don't even set the italic if the font didn't exist on the system
6239                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
6240             }
6241             break;
6242         }
6243     }
6245     if (!newFontSpec.empty()) {
6246         sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str());
6247     }
6249     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6250     if (result_fontspec == QUERY_STYLE_NOTHING)
6251     {
6252         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6253         prefs->setStyle("/tools/text/style", css);
6254     }
6256     sp_style_unref(query);
6258     sp_desktop_set_style (desktop, css, true, true);
6259     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6260                                    _("Text: Change font style"));
6261     sp_repr_css_attr_unref (css);
6263     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6266 void
6267 sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
6268                                      gpointer         data)
6270     if (g_object_get_data (G_OBJECT (button), "block")) {
6271         g_object_set_data (G_OBJECT (button), "block", gpointer(0));
6272         return;
6273     }
6275     SPDesktop   *desktop    = SP_ACTIVE_DESKTOP;
6276     SPCSSAttr   *css        = sp_repr_css_attr_new ();
6277     int          prop       = GPOINTER_TO_INT(data);
6279     switch (prop)
6280     {
6281         case 0:
6282         {
6283             sp_repr_css_set_property (css, "writing-mode", "lr");
6284             break;
6285         }
6287         case 1:
6288         {
6289             sp_repr_css_set_property (css, "writing-mode", "tb");
6290             break;
6291         }
6292     }
6294     SPStyle *query =
6295         sp_style_new (SP_ACTIVE_DOCUMENT);
6296     int result_numbers =
6297         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6299     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6300     if (result_numbers == QUERY_STYLE_NOTHING)
6301     {
6302         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6303         prefs->setStyle("/tools/text/style", css);
6304     }
6306     sp_desktop_set_style (desktop, css, true, true);
6307     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
6308                                    _("Text: Change orientation"));
6309     sp_repr_css_attr_unref (css);
6311     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6314 gboolean
6315 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6317     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6318     if (!desktop) return FALSE;
6320     switch (get_group0_keyval (event)) {
6321         case GDK_Escape: // defocus
6322             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6323             sp_text_toolbox_selection_changed (NULL, tbl); // update
6324             return TRUE; // I consumed the event
6325             break;
6326     }
6327     return FALSE;
6330 gboolean
6331 sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject */*tbl*/)
6333     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6334     if (!desktop) return FALSE;
6336     switch (get_group0_keyval (event)) {
6337         case GDK_KP_Enter:
6338         case GDK_Return:
6339         case GDK_Escape: // defocus
6340             gtk_widget_hide (w);
6341             popdown_visible = false;
6342             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6343             return TRUE; // I consumed the event
6344             break;
6345         case GDK_w:
6346         case GDK_W:
6347             if (event->state & GDK_CONTROL_MASK) {
6348                 gtk_widget_hide (w);
6349                 popdown_visible = false;
6350                 gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6351                 return TRUE; // I consumed the event
6352             }
6353             break;
6354     }
6355     return FALSE;
6359 void
6360 sp_text_toolbox_size_changed  (GtkComboBox *cbox,
6361                                GObject     *tbl)
6363     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6365     if (g_object_get_data (tbl, "size-block")) return;
6367     // If this is not from selecting a size in the list (in which case get_active will give the
6368     // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
6369     // process this event. This fixes GTK's stupid insistence on sending an activate change every
6370     // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
6371     if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
6372         return;
6374     gdouble value = -1;
6375     {
6376         gchar *endptr;
6377         gchar *const text = gtk_combo_box_get_active_text(cbox);
6378         if (text) {
6379             value = g_strtod(text, &endptr);
6380             if (endptr == text) {  // Conversion failed, non-numeric input.
6381                 value = -1;
6382             }
6383             g_free(text);
6384         }
6385     }
6386     if (value <= 0) {
6387         return; // could not parse value
6388     }
6390     SPCSSAttr *css = sp_repr_css_attr_new ();
6391     Inkscape::CSSOStringStream osfs;
6392     osfs << value;
6393     sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
6395     SPStyle *query =
6396         sp_style_new (SP_ACTIVE_DOCUMENT);
6397     int result_numbers =
6398         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
6400     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
6401     if (result_numbers == QUERY_STYLE_NOTHING)
6402     {
6403         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6404         prefs->setStyle("/tools/text/style", css);
6405     }
6407     sp_style_unref(query);
6409     sp_desktop_set_style (desktop, css, true, true);
6410     sp_document_maybe_done (sp_desktop_document (SP_ACTIVE_DESKTOP), "ttb:size", SP_VERB_NONE,
6411                                    _("Text: Change font size"));
6412     sp_repr_css_attr_unref (css);
6414     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6417 gboolean
6418 sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus */*event*/, GObject *tbl)
6420     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6421     if (!desktop) return FALSE;
6423     if (!g_object_get_data (tbl, "esc-pressed")) {
6424         g_object_set_data (tbl, "enter-pressed", gpointer(1));
6425         GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6426         sp_text_toolbox_size_changed (cbox, tbl);
6427         g_object_set_data (tbl, "enter-pressed", gpointer(0));
6428     }
6429     return FALSE; // I consumed the event
6433 gboolean
6434 sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
6436     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6437     if (!desktop) return FALSE;
6439     switch (get_group0_keyval (event)) {
6440         case GDK_Escape: // defocus
6441             g_object_set_data (tbl, "esc-pressed", gpointer(1));
6442             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6443             g_object_set_data (tbl, "esc-pressed", gpointer(0));
6444             return TRUE; // I consumed the event
6445             break;
6446         case GDK_Return: // defocus
6447         case GDK_KP_Enter:
6448             g_object_set_data (tbl, "enter-pressed", gpointer(1));
6449             GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
6450             sp_text_toolbox_size_changed (cbox, tbl);
6451             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6452             g_object_set_data (tbl, "enter-pressed", gpointer(0));
6453             return TRUE; // I consumed the event
6454             break;
6455     }
6456     return FALSE;
6459 void
6460 sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
6461                                          GObject            *tbl)
6463     GtkWidget *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
6464     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
6465     int x, y;
6467     if (!popdown_visible)
6468     {
6469         gdk_window_get_origin (widget->window, &x, &y);
6470         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
6471         gtk_widget_show_all (popdown);
6472         //sp_transientize (popdown);
6474         gdk_pointer_grab (widget->window, TRUE,
6475                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
6476                                         GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
6477                                         GDK_POINTER_MOTION_MASK),
6478                           NULL, NULL, GDK_CURRENT_TIME);
6480         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
6482         popdown_visible = true;
6483     }
6484     else
6485     {
6486         SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6487         gdk_pointer_ungrab (GDK_CURRENT_TIME);
6488         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
6489         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6490         gtk_widget_hide (popdown);
6491         popdown_visible = false;
6492     }
6495 gboolean
6496 sp_text_toolbox_entry_focus_in  (GtkWidget        *entry,
6497                                  GdkEventFocus    */*event*/,
6498                                  GObject          */*tbl*/)
6500     gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
6501     return FALSE;
6504 gboolean
6505 sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
6506                                    GdkEventFocus    */*event*/,
6507                                    GObject          */*tbl*/)
6509     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
6511     if (popdown_hasfocus) {
6512         gtk_widget_hide (popdown);
6513         popdown_hasfocus = false;
6514         popdown_visible = false;
6515         gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
6516         return TRUE;
6517     }
6518     return FALSE;
6521 gboolean
6522 sp_text_toolbox_popdown_focus_in (GtkWidget        */*popdown*/,
6523                                    GdkEventFocus    */*event*/,
6524                                    GObject          */*tbl*/)
6526     popdown_hasfocus = true;
6527     return TRUE;
6531 void
6532 cell_data_func  (GtkTreeViewColumn */*column*/,
6533                  GtkCellRenderer   *cell,
6534                  GtkTreeModel      *tree_model,
6535                  GtkTreeIter       *iter,
6536                  gpointer           /*data*/)
6538     gchar *family;
6539     gtk_tree_model_get(tree_model, iter, 0, &family, -1);
6540     gchar *const family_escaped = g_markup_escape_text(family, -1);
6542     static char const *const sample = _("AaBbCcIiPpQq12369$\342\202\254\302\242?.;/()");
6543     gchar *const sample_escaped = g_markup_escape_text(sample, -1);
6545     std::stringstream markup;
6546     markup << family_escaped << "  <span foreground='darkgray' font_family='"
6547            << family_escaped << "'>" << sample_escaped << "</span>";
6548     g_object_set (G_OBJECT (cell), "markup", markup.str().c_str(), NULL);
6550     g_free(family);
6551     g_free(family_escaped);
6552     g_free(sample_escaped);
6555 static void delete_completion(GObject */*obj*/, GtkWidget *entry) {
6556     GObject *completion = (GObject *) gtk_object_get_data(GTK_OBJECT(entry), "completion");
6557     if (completion) {
6558         gtk_entry_set_completion (GTK_ENTRY(entry), NULL);
6559         g_object_unref (completion);
6560     }
6563 GtkWidget*
6564 sp_text_toolbox_new (SPDesktop *desktop)
6566     GtkToolbar   *tbl = GTK_TOOLBAR(gtk_toolbar_new());
6567     GtkIconSize secondarySize = static_cast<GtkIconSize>(prefToSize("/toolbox/secondary", 1));
6569     gtk_object_set_data(GTK_OBJECT(tbl), "dtw", desktop->canvas);
6570     gtk_object_set_data(GTK_OBJECT(tbl), "desktop", desktop);
6572     GtkTooltips *tt = gtk_tooltips_new();
6573     Glib::RefPtr<Gtk::ListStore> store = Inkscape::FontLister::get_instance()->get_font_list();
6575     ////////////Family
6576     //Window
6577     GtkWidget   *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
6578     gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
6580     //Entry
6581     GtkWidget           *entry = gtk_entry_new ();
6582     gtk_object_set_data(GTK_OBJECT(entry), "altx-text", entry);
6583     GtkEntryCompletion  *completion = gtk_entry_completion_new ();
6584     gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (Glib::unwrap(store)));
6585     gtk_entry_completion_set_text_column (completion, 0);
6586     gtk_entry_completion_set_minimum_key_length (completion, 1);
6587     g_object_set (G_OBJECT(completion), "inline-completion", TRUE, "popup-completion", TRUE, NULL);
6588     gtk_entry_set_completion (GTK_ENTRY(entry), completion);
6589     gtk_object_set_data(GTK_OBJECT(entry), "completion", completion);
6590     gtk_toolbar_append_widget( tbl, entry, "", "" );
6591     g_signal_connect(G_OBJECT(tbl), "destroy", G_CALLBACK(delete_completion), entry);
6593     //Button
6594     GtkWidget   *button = gtk_button_new ();
6595     gtk_container_add       (GTK_CONTAINER (button), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
6596     gtk_toolbar_append_widget( tbl, button, "", "");
6598     //Popdown
6599     GtkWidget           *sw = gtk_scrolled_window_new (NULL, NULL);
6600     GtkWidget           *treeview = gtk_tree_view_new ();
6602     GtkCellRenderer     *cell = gtk_cell_renderer_text_new ();
6603     GtkTreeViewColumn   *column = gtk_tree_view_column_new ();
6604     gtk_tree_view_column_pack_start (column, cell, FALSE);
6605     gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
6606     gtk_tree_view_column_set_cell_data_func (column, cell, GtkTreeCellDataFunc (cell_data_func), NULL, NULL);
6607     gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
6609     gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (Glib::unwrap(store)));
6610     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
6611     gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (treeview), TRUE);
6613     //gtk_tree_view_set_enable_search (GTK_TREE_VIEW (treeview), TRUE);
6615     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
6616     gtk_container_add (GTK_CONTAINER (sw), treeview);
6618     gtk_container_add (GTK_CONTAINER (window), sw);
6619     gtk_widget_set_size_request (window, 300, 450);
6621     g_signal_connect (G_OBJECT (entry),  "activate", G_CALLBACK (sp_text_toolbox_family_entry_activate), tbl);
6622     g_signal_connect (G_OBJECT (entry),  "focus-in-event", G_CALLBACK (sp_text_toolbox_entry_focus_in), tbl);
6623     g_signal_connect (G_OBJECT (entry), "key-press-event", G_CALLBACK(sp_text_toolbox_family_keypress), tbl);
6625     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
6627     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
6628     g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
6629     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
6631     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
6632     g_signal_connect (G_OBJECT (tselection), "changed", G_CALLBACK (sp_text_toolbox_family_changed), tbl);
6634     g_object_set_data (G_OBJECT (tbl), "family-entry", entry);
6635     g_object_set_data (G_OBJECT (tbl), "family-popdown-button", button);
6636     g_object_set_data (G_OBJECT (tbl), "family-popdown-window", window);
6637     g_object_set_data (G_OBJECT (tbl), "family-tree-selection", tselection);
6638     g_object_set_data (G_OBJECT (tbl), "family-tree-view", treeview);
6640     GtkWidget *image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, secondarySize);
6641     GtkWidget *box = gtk_event_box_new ();
6642     gtk_container_add (GTK_CONTAINER (box), image);
6643     gtk_toolbar_append_widget( tbl, box, "", "");
6644     g_object_set_data (G_OBJECT (tbl), "warning-image", box);
6645     GtkTooltips *tooltips = gtk_tooltips_new ();
6646     gtk_tooltips_set_tip (tooltips, box, _("This font is currently not installed on your system. Inkscape will use the default font instead."), "");
6647     gtk_widget_hide (GTK_WIDGET (box));
6648     g_signal_connect_swapped (G_OBJECT (tbl), "show", G_CALLBACK (gtk_widget_hide), box);
6650     ////////////Size
6651     gchar const *const sizes[] = {
6652         "4", "6", "8", "9", "10", "11", "12", "13", "14",
6653         "16", "18", "20", "22", "24", "28",
6654         "32", "36", "40", "48", "56", "64", "72", "144"
6655     };
6657     GtkWidget *cbox = gtk_combo_box_entry_new_text ();
6658     for (unsigned int i = 0; i < G_N_ELEMENTS(sizes); ++i) {
6659         gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), sizes[i]);
6660     }
6661     gtk_widget_set_size_request (cbox, 80, -1);
6662     gtk_toolbar_append_widget( tbl, cbox, "", "");
6663     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
6664     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
6665     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
6666     gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
6668     ////////////Text anchor
6669     GtkWidget *group   = gtk_radio_button_new (NULL);
6670     GtkWidget *row     = gtk_hbox_new (FALSE, 4);
6671     g_object_set_data (G_OBJECT (tbl), "anchor-group", group);
6673     // left
6674     GtkWidget *rbutton = group;
6675     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6676     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_LEFT, secondarySize));
6677     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6679     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6680     g_object_set_data   (G_OBJECT (tbl), "text-start", rbutton);
6681     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(0));
6682     gtk_tooltips_set_tip(tt, rbutton, _("Align left"), NULL);
6684     // center
6685     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6686     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6687     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_CENTER, secondarySize));
6688     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6690     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6691     g_object_set_data   (G_OBJECT (tbl), "text-middle", rbutton);
6692     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer (1));
6693     gtk_tooltips_set_tip(tt, rbutton, _("Center"), NULL);
6695     // right
6696     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6697     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6698     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_RIGHT, secondarySize));
6699     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6701     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6702     g_object_set_data   (G_OBJECT (tbl), "text-end", rbutton);
6703     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(2));
6704     gtk_tooltips_set_tip(tt, rbutton, _("Align right"), NULL);
6706     // fill
6707     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6708     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6709     gtk_container_add           (GTK_CONTAINER (rbutton), gtk_image_new_from_stock (GTK_STOCK_JUSTIFY_FILL, secondarySize));
6710     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6712     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6713     g_object_set_data   (G_OBJECT (tbl), "text-fill", rbutton);
6714     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_anchoring_toggled), gpointer(3));
6715     gtk_tooltips_set_tip(tt, rbutton, _("Justify"), NULL);
6717     gtk_toolbar_append_widget( tbl, row, "", "");
6719     //spacer
6720     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6722     ////////////Text style
6723     row = gtk_hbox_new (FALSE, 4);
6725     // bold
6726     rbutton = gtk_toggle_button_new ();
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_BOLD, secondarySize));
6729     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6730     gtk_tooltips_set_tip(tt, rbutton, _("Bold"), NULL);
6732     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6733     g_object_set_data   (G_OBJECT (tbl), "style-bold", rbutton);
6734     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer(0));
6736     // italic
6737     rbutton = gtk_toggle_button_new ();
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_ITALIC, secondarySize));
6740     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6741     gtk_tooltips_set_tip(tt, rbutton, _("Italic"), NULL);
6743     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6744     g_object_set_data   (G_OBJECT (tbl), "style-italic", rbutton);
6745     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_style_toggled), gpointer (1));
6747     gtk_toolbar_append_widget( tbl, row, "", "");
6749     //spacer
6750     gtk_toolbar_append_widget( tbl, gtk_vseparator_new(), "", "" );
6752     // Text orientation
6753     group   = gtk_radio_button_new (NULL);
6754     row     = gtk_hbox_new (FALSE, 4);
6755     g_object_set_data (G_OBJECT (tbl), "orientation-group", group);
6757     // horizontal
6758     rbutton = group;
6759     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6760     gtk_container_add           (GTK_CONTAINER (rbutton),
6761                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_HORIZONTAL));
6762     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6763     gtk_tooltips_set_tip(tt, rbutton, _("Horizontal text"), NULL);
6765     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6766     g_object_set_data   (G_OBJECT (tbl), "orientation-horizontal", rbutton);
6767     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer(0));
6769     // vertical
6770     rbutton = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
6771     gtk_button_set_relief       (GTK_BUTTON (rbutton), GTK_RELIEF_NONE);
6772     gtk_container_add           (GTK_CONTAINER (rbutton),
6773                                  sp_icon_new (static_cast<Inkscape::IconSize>(secondarySize), INKSCAPE_ICON_FORMAT_TEXT_DIRECTION_VERTICAL));
6774     gtk_toggle_button_set_mode  (GTK_TOGGLE_BUTTON (rbutton), FALSE);
6775     gtk_tooltips_set_tip(tt, rbutton, _("Vertical text"), NULL);
6777     gtk_box_pack_start  (GTK_BOX  (row), rbutton, FALSE, FALSE, 0);
6778     g_object_set_data   (G_OBJECT (tbl), "orientation-vertical", rbutton);
6779     g_signal_connect    (G_OBJECT (rbutton), "toggled", G_CALLBACK (sp_text_toolbox_orientation_toggled), gpointer (1));
6780     gtk_toolbar_append_widget( tbl, row, "", "" );
6783     //watch selection
6784     Inkscape::ConnectionPool* pool = Inkscape::ConnectionPool::new_connection_pool ("ISTextToolbox");
6786     sigc::connection *c_selection_changed =
6787         new sigc::connection (sp_desktop_selection (desktop)->connectChanged
6788                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_changed), (GObject*)tbl)));
6789     pool->add_connection ("selection-changed", c_selection_changed);
6791     sigc::connection *c_selection_modified =
6792         new sigc::connection (sp_desktop_selection (desktop)->connectModified
6793                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_selection_modified), (GObject*)tbl)));
6794     pool->add_connection ("selection-modified", c_selection_modified);
6796     sigc::connection *c_subselection_changed =
6797         new sigc::connection (desktop->connectToolSubselectionChanged
6798                               (sigc::bind (sigc::ptr_fun (sp_text_toolbox_subselection_changed), (GObject*)tbl)));
6799     pool->add_connection ("tool-subselection-changed", c_subselection_changed);
6801     Inkscape::ConnectionPool::connect_destroy (G_OBJECT (tbl), pool);
6804     gtk_widget_show_all( GTK_WIDGET(tbl) );
6806     return GTK_WIDGET(tbl);
6807 } // end of sp_text_toolbox_new()
6809 }//<unnamed> namespace
6812 //#########################
6813 //##      Connector      ##
6814 //#########################
6816 static void sp_connector_path_set_avoid(void)
6818     cc_selection_set_avoid(true);
6822 static void sp_connector_path_set_ignore(void)
6824     cc_selection_set_avoid(false);
6829 static void connector_spacing_changed(GtkAdjustment *adj, GObject* tbl)
6831     // quit if run by the _changed callbacks
6832     if (g_object_get_data( tbl, "freeze" )) {
6833         return;
6834     }
6836     SPDesktop *desktop = (SPDesktop *) g_object_get_data( tbl, "desktop" );
6837     SPDocument *doc = sp_desktop_document(desktop);
6839     if (!sp_document_get_undo_sensitive(doc))
6840     {
6841         return;
6842     }
6844     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
6846     if ( repr->attribute("inkscape:connector-spacing") ) {
6847         gdouble priorValue = gtk_adjustment_get_value(adj);
6848         sp_repr_get_double( repr, "inkscape:connector-spacing", &priorValue );
6849         if ( priorValue == gtk_adjustment_get_value(adj) ) {
6850             return;
6851         }
6852     } else if ( adj->value == defaultConnSpacing ) {
6853         return;
6854     }
6856     // in turn, prevent callbacks from responding
6857     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(TRUE) );
6859     sp_repr_set_css_double(repr, "inkscape:connector-spacing", adj->value);
6860     SP_OBJECT(desktop->namedview)->updateRepr();
6862     GSList *items = get_avoided_items(NULL, desktop->currentRoot(), desktop);
6863     for ( GSList const *iter = items ; iter != NULL ; iter = iter->next ) {
6864         SPItem *item = reinterpret_cast<SPItem *>(iter->data);
6865         Geom::Matrix m = Geom::identity();
6866         avoid_item_move(&m, item);
6867     }
6869     if (items) {
6870         g_slist_free(items);
6871     }
6873     sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
6874             _("Change connector spacing"));
6876     g_object_set_data( tbl, "freeze", GINT_TO_POINTER(FALSE) );
6878     spinbutton_defocus(GTK_OBJECT(tbl));
6881 static void sp_connector_graph_layout(void)
6883     if (!SP_ACTIVE_DESKTOP) return;
6884     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6886     // hack for clones, see comment in align-and-distribute.cpp
6887     int saved_compensation = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6888     prefs->setInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_UNMOVED);
6890     graphlayout(sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList());
6892     prefs->setInt("/options/clonecompensation/value", saved_compensation);
6894     sp_document_done(sp_desktop_document(SP_ACTIVE_DESKTOP), SP_VERB_DIALOG_ALIGN_DISTRIBUTE, _("Arrange connector network"));
6897 static void sp_directed_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6899     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6900     prefs->setBool("/tools/connector/directedlayout",
6901                 gtk_toggle_action_get_active( act ));
6904 static void sp_nooverlaps_graph_layout_toggled( GtkToggleAction* act, GtkObject */*tbl*/ )
6906     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6907     prefs->setBool("/tools/connector/avoidoverlaplayout",
6908                 gtk_toggle_action_get_active( act ));
6912 static void connector_length_changed(GtkAdjustment *adj, GObject* tbl)
6914     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6915     prefs->setDouble("/tools/connector/length", adj->value);
6916     spinbutton_defocus(GTK_OBJECT(tbl));
6919 static void connector_tb_event_attr_changed(Inkscape::XML::Node *repr,
6920                                             gchar const *name, gchar const */*old_value*/, gchar const */*new_value*/,
6921                                             bool /*is_interactive*/, gpointer data)
6923     GtkWidget *tbl = GTK_WIDGET(data);
6925     if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
6926         return;
6927     }
6928     if (strcmp(name, "inkscape:connector-spacing") != 0) {
6929         return;
6930     }
6932     GtkAdjustment *adj = (GtkAdjustment*)
6933             gtk_object_get_data(GTK_OBJECT(tbl), "spacing");
6934     gdouble spacing = defaultConnSpacing;
6935     sp_repr_get_double(repr, "inkscape:connector-spacing", &spacing);
6937     gtk_adjustment_set_value(adj, spacing);
6941 static Inkscape::XML::NodeEventVector connector_tb_repr_events = {
6942     NULL, /* child_added */
6943     NULL, /* child_removed */
6944     connector_tb_event_attr_changed,
6945     NULL, /* content_changed */
6946     NULL  /* order_changed */
6947 };
6950 static void sp_connector_toolbox_prep( SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder )
6952     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
6953     Inkscape::IconSize secondarySize = prefToSize("/toolbox/secondary", 1);
6955     {
6956         InkAction* inky = ink_action_new( "ConnectorAvoidAction",
6957                                           _("Avoid"),
6958                                           _("Make connectors avoid selected objects"),
6959                                           INKSCAPE_ICON_CONNECTOR_AVOID,
6960                                           secondarySize );
6961         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_avoid), holder );
6962         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6963     }
6965     {
6966         InkAction* inky = ink_action_new( "ConnectorIgnoreAction",
6967                                           _("Ignore"),
6968                                           _("Make connectors ignore selected objects"),
6969                                           INKSCAPE_ICON_CONNECTOR_IGNORE,
6970                                           secondarySize );
6971         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_path_set_ignore), holder );
6972         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6973     }
6975     EgeAdjustmentAction* eact = 0;
6977     // Spacing spinbox
6978     eact = create_adjustment_action( "ConnectorSpacingAction",
6979                                      _("Connector Spacing"), _("Spacing:"),
6980                                      _("The amount of space left around objects by auto-routing connectors"),
6981                                      "/tools/connector/spacing", defaultConnSpacing,
6982                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-spacing",
6983                                      0, 100, 1.0, 10.0,
6984                                      0, 0, 0,
6985                                      connector_spacing_changed, 1, 0 );
6986     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
6988     // Graph (connector network) layout
6989     {
6990         InkAction* inky = ink_action_new( "ConnectorGraphAction",
6991                                           _("Graph"),
6992                                           _("Nicely arrange selected connector network"),
6993                                           INKSCAPE_ICON_DISTRIBUTE_GRAPH,
6994                                           secondarySize );
6995         g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(sp_connector_graph_layout), holder );
6996         gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
6997     }
6999     // Default connector length spinbox
7000     eact = create_adjustment_action( "ConnectorLengthAction",
7001                                      _("Connector Length"), _("Length:"),
7002                                      _("Ideal length for connectors when layout is applied"),
7003                                      "/tools/connector/length", 100,
7004                                      GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "inkscape:connector-length",
7005                                      10, 1000, 10.0, 100.0,
7006                                      0, 0, 0,
7007                                      connector_length_changed, 1, 0 );
7008     gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7011     // Directed edges toggle button
7012     {
7013         InkToggleAction* act = ink_toggle_action_new( "ConnectorDirectedAction",
7014                                                       _("Downwards"),
7015                                                       _("Make connectors with end-markers (arrows) point downwards"),
7016                                                       INKSCAPE_ICON_DISTRIBUTE_GRAPH_DIRECTED,
7017                                                       Inkscape::ICON_SIZE_DECORATION );
7018         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7020         bool tbuttonstate = prefs->getBool("/tools/connector/directedlayout");
7021         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), ( tbuttonstate ? TRUE : FALSE ));
7023         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_directed_graph_layout_toggled), holder );
7024     }
7026     // Avoid overlaps toggle button
7027     {
7028         InkToggleAction* act = ink_toggle_action_new( "ConnectorOverlapAction",
7029                                                       _("Remove overlaps"),
7030                                                       _("Do not allow overlapping shapes"),
7031                                                       INKSCAPE_ICON_DISTRIBUTE_REMOVE_OVERLAPS,
7032                                                       Inkscape::ICON_SIZE_DECORATION );
7033         gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
7035         bool tbuttonstate = prefs->getBool("/tools/connector/avoidoverlaplayout");
7036         gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), (tbuttonstate ? TRUE : FALSE ));
7038         g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(sp_nooverlaps_graph_layout_toggled), holder );
7039     }
7041     // Code to watch for changes to the connector-spacing attribute in
7042     // the XML.
7043     Inkscape::XML::Node *repr = SP_OBJECT_REPR(desktop->namedview);
7044     g_assert(repr != NULL);
7046     purge_repr_listener( holder, holder );
7048     if (repr) {
7049         g_object_set_data( holder, "repr", repr );
7050         Inkscape::GC::anchor(repr);
7051         sp_repr_add_listener( repr, &connector_tb_repr_events, holder );
7052         sp_repr_synthesize_events( repr, &connector_tb_repr_events, holder );
7053     }
7054 } // end of sp_connector_toolbox_prep()
7057 //#########################
7058 //##     Paintbucket     ##
7059 //#########################
7061 static void paintbucket_channels_changed(EgeSelectOneAction* act, GObject* /*tbl*/)
7063     gint channels = ege_select_one_action_get_active( act );
7064     flood_channels_set_channels( channels );
7067 static void paintbucket_threshold_changed(GtkAdjustment *adj, GObject */*tbl*/)
7069     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7070     prefs->setInt("/tools/paintbucket/threshold", (gint)adj->value);
7073 static void paintbucket_autogap_changed(EgeSelectOneAction* act, GObject */*tbl*/)
7075     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7076     prefs->setBool("/tools/paintbucket/autogap", ege_select_one_action_get_active( act ));
7079 static void paintbucket_offset_changed(GtkAdjustment *adj, GObject *tbl)
7081     UnitTracker* tracker = static_cast<UnitTracker*>(g_object_get_data( tbl, "tracker" ));
7082     SPUnit const *unit = tracker->getActiveUnit();
7083     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7085     prefs->setDouble("/tools/paintbucket/offset", (gdouble)sp_units_get_pixels(adj->value, *unit));
7086     prefs->setString("/tools/paintbucket/offsetunits", sp_unit_get_abbreviation(unit));
7089 static void paintbucket_defaults (GtkWidget *, GObject *tbl)
7091     // FIXME: make defaults settable via Inkscape Options
7092     struct KeyValue {
7093         char const *key;
7094         double value;
7095     } const key_values[] = {
7096         {"threshold", 15},
7097         {"offset", 0.0}
7098     };
7100     for (unsigned i = 0; i < G_N_ELEMENTS(key_values); ++i) {
7101         KeyValue const &kv = key_values[i];
7102         GtkAdjustment* adj = static_cast<GtkAdjustment *>(g_object_get_data(tbl, kv.key));
7103         if ( adj ) {
7104             gtk_adjustment_set_value(adj, kv.value);
7105         }
7106     }
7108     EgeSelectOneAction* channels_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "channels_action" ) );
7109     ege_select_one_action_set_active( channels_action, FLOOD_CHANNELS_RGB );
7110     EgeSelectOneAction* autogap_action = EGE_SELECT_ONE_ACTION( g_object_get_data (tbl, "autogap_action" ) );
7111     ege_select_one_action_set_active( autogap_action, 0 );
7114 static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
7116     EgeAdjustmentAction* eact = 0;
7117     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
7119     {
7120         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7122         GList* items = 0;
7123         gint count = 0;
7124         for ( items = flood_channels_dropdown_items_list(); items ; items = g_list_next(items) )
7125         {
7126             GtkTreeIter iter;
7127             gtk_list_store_append( model, &iter );
7128             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7129             count++;
7130         }
7131         g_list_free( items );
7132         items = 0;
7133         EgeSelectOneAction* act1 = ege_select_one_action_new( "ChannelsAction", _("Fill by"), (""), NULL, GTK_TREE_MODEL(model) );
7134         g_object_set( act1, "short_label", _("Fill by:"), NULL );
7135         ege_select_one_action_set_appearance( act1, "compact" );
7136         ege_select_one_action_set_active( act1, prefs->getInt("/tools/paintbucket/channels", 0) );
7137         g_signal_connect( G_OBJECT(act1), "changed", G_CALLBACK(paintbucket_channels_changed), holder );
7138         gtk_action_group_add_action( mainActions, GTK_ACTION(act1) );
7139         g_object_set_data( holder, "channels_action", act1 );
7140     }
7142     // Spacing spinbox
7143     {
7144         eact = create_adjustment_action(
7145             "ThresholdAction",
7146             _("Fill Threshold"), _("Threshold:"),
7147             _("The maximum allowed difference between the clicked pixel and the neighboring pixels to be counted in the fill"),
7148             "/tools/paintbucket/threshold", 5, GTK_WIDGET(desktop->canvas), NULL, holder, TRUE,
7149             "inkscape:paintbucket-threshold", 0, 100.0, 1.0, 0.0,
7150             0, 0, 0,
7151             paintbucket_threshold_changed, 1, 0 );
7153         ege_adjustment_action_set_appearance( eact, TOOLBAR_SLIDER_HINT );
7154         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7155     }
7157     // Create the units menu.
7158     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
7159     Glib::ustring stored_unit = prefs->getString("/tools/paintbucket/offsetunits");
7160     if (!stored_unit.empty())
7161         tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit.data()));
7162     g_object_set_data( holder, "tracker", tracker );
7163     {
7164         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
7165         gtk_action_group_add_action( mainActions, act );
7166     }
7168     // Offset spinbox
7169     {
7170         eact = create_adjustment_action(
7171             "OffsetAction",
7172             _("Grow/shrink by"), _("Grow/shrink by:"),
7173             _("The amount to grow (positive) or shrink (negative) the created fill path"),
7174             "/tools/paintbucket/offset", 0, GTK_WIDGET(desktop->canvas), NULL/*us*/, holder, TRUE,
7175             "inkscape:paintbucket-offset", -1e6, 1e6, 0.1, 0.5,
7176             0, 0, 0,
7177             paintbucket_offset_changed, 1, 2);
7178         tracker->addAdjustment( ege_adjustment_action_get_adjustment(eact) );
7180         gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
7181     }
7183     /* Auto Gap */
7184     {
7185         GtkListStore* model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
7187         GList* items = 0;
7188         gint count = 0;
7189         for ( items = flood_autogap_dropdown_items_list(); items ; items = g_list_next(items) )
7190         {
7191             GtkTreeIter iter;
7192             gtk_list_store_append( model, &iter );
7193             gtk_list_store_set( model, &iter, 0, reinterpret_cast<gchar*>(items->data), 1, count, -1 );
7194             count++;
7195         }
7196         g_list_free( items );
7197         items = 0;
7198         EgeSelectOneAction* act2 = ege_select_one_action_new( "AutoGapAction", _("Close gaps"), (""), NULL, GTK_TREE_MODEL(model) );
7199         g_object_set( act2, "short_label", _("Close gaps:"), NULL );
7200         ege_select_one_action_set_appearance( act2, "compact" );
7201         ege_select_one_action_set_active( act2, prefs->getBool("/tools/paintbucket/autogap") );
7202         g_signal_connect( G_OBJECT(act2), "changed", G_CALLBACK(paintbucket_autogap_changed), holder );
7203         gtk_action_group_add_action( mainActions, GTK_ACTION(act2) );
7204         g_object_set_data( holder, "autogap_action", act2 );
7205     }
7207     /* Reset */
7208     {
7209         GtkAction* act = gtk_action_new( "PaintbucketResetAction",
7210                                           _("Defaults"),
7211                                           _("Reset paint bucket parameters to defaults (use Inkscape Preferences > Tools to change defaults)"),
7212                                           GTK_STOCK_CLEAR );
7213         g_signal_connect_after( G_OBJECT(act), "activate", G_CALLBACK(paintbucket_defaults), holder );
7214         gtk_action_group_add_action( mainActions, act );
7215         gtk_action_set_sensitive( act, TRUE );
7216     }
7220 /*
7221   Local Variables:
7222   mode:c++
7223   c-file-style:"stroustrup"
7224   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
7225   indent-tabs-mode:nil
7226   fill-column:99
7227   End:
7228 */
7229 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :